DO NOT MERGE : Fix build breakage due to 2d516d2d46b1b1. am: 547c7d9a0b  -s ours am: 4732ec6440  -s ours am: 59c7fe7a39 am: 28a7d07705 am: a413ac3c8a am: 685a175cb0  -s ours am: 886eb4351e am: 4f22203279 am: ea729dcae5 am: 369bf9065d am: 79f474a679  -s ours am: c26850c970
am: 8085ab4ec5

Change-Id: I44895fdb1432b97afbf692be6eaf037f8e3a52d1
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5b5eff4..304ce4e 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -60,3 +60,4 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
diff --git a/adb/Android.mk b/adb/Android.mk
index 6ed01fa..2b6df70 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -108,7 +108,6 @@
     sysdeps_win32_test.cpp \
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libadbd_usb
 LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
 LOCAL_SRC_FILES := daemon/usb.cpp
@@ -117,12 +116,11 @@
 
 # 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
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libasyncio
 
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := libadbd
 LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
 LOCAL_SRC_FILES := \
@@ -171,7 +169,6 @@
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_MODULE := adbd_test
 LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
 LOCAL_SRC_FILES := \
@@ -330,8 +327,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_CLANG := true
-
 LOCAL_SRC_FILES := \
     daemon/main.cpp \
     daemon/mdns.cpp \
@@ -360,13 +355,12 @@
 LOCAL_MODULE := adbd
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STRIP_MODULE := keep_symbols
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
+    libasyncio \
     libavb_user \
     libbase \
     libqemu_pipe \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index bfb1144..6b30be8 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -49,6 +49,7 @@
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_listeners.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "sysdeps/chrono.h"
 #include "transport.h"
@@ -256,19 +257,6 @@
     send_packet(cp, t);
 }
 
-#if ADB_HOST
-
-void SendConnectOnHost(atransport* t) {
-    // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
-    // host and ep_in on device may not be the same.
-    apacket* p = get_apacket();
-    CHECK(p);
-    send_packet(p, t);
-    send_connect(t);
-}
-
-#endif
-
 // qual_overwrite is used to overwrite a qualifier string.  dst is a
 // pointer to a char pointer.  It is assumed that if *dst is non-NULL, it
 // was malloc'ed and needs to freed.  *dst will be set to a dup of src.
@@ -369,7 +357,7 @@
         if (p->msg.arg0){
             send_packet(p, t);
 #if ADB_HOST
-            SendConnectOnHost(t);
+            send_connect(t);
 #endif
         } else {
             t->SetConnectionState(kCsOffline);
@@ -656,6 +644,26 @@
 
 #endif
 
+static void ReportServerStartupFailure(pid_t pid) {
+    fprintf(stderr, "ADB server didn't ACK\n");
+    fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
+    fprintf(stderr, "Server had pid: %d\n", pid);
+
+    unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY));
+    if (fd == -1) return;
+
+    // Let's not show more than 128KiB of log...
+    adb_lseek(fd, -128 * 1024, SEEK_END);
+    std::string content;
+    if (!android::base::ReadFdToString(fd, &content)) return;
+
+    std::string header = android::base::StringPrintf("--- adb starting (pid %d) ---", pid);
+    std::vector<std::string> lines = android::base::Split(content, "\n");
+    int i = lines.size() - 1;
+    while (i >= 0 && lines[i] != header) --i;
+    while (static_cast<size_t>(i) < lines.size()) fprintf(stderr, "%s\n", lines[i++].c_str());
+}
+
 int launch_server(const std::string& socket_spec) {
 #if defined(_WIN32)
     /* we need to start the server in the background                    */
@@ -835,7 +843,8 @@
                 memcmp(temp, expected, expected_length) == 0) {
                 got_ack = true;
             } else {
-                fprintf(stderr, "ADB server didn't ACK\n");
+                ReportServerStartupFailure(GetProcessId(process_handle.get()));
+                return -1;
             }
         } else {
             const DWORD err = GetLastError();
@@ -909,12 +918,9 @@
                            "--reply-fd", reply_fd, NULL);
         // this should not return
         fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
-    } else  {
+    } else {
         // parent side of the fork
-
-        char  temp[3];
-
-        temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+        char temp[3] = {};
         // wait for the "OK\n" message
         adb_close(fd[1]);
         int ret = adb_read(fd[0], temp, 3);
@@ -925,7 +931,7 @@
             return -1;
         }
         if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
-            fprintf(stderr, "ADB server didn't ACK\n" );
+            ReportServerStartupFailure(pid);
             return -1;
         }
     }
@@ -937,8 +943,8 @@
 // Try to handle a network forwarding request.
 // This returns 1 on success, 0 on failure, and -1 to indicate this is not
 // a forwarding-related request.
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
-{
+int handle_forward_request(const char* service, TransportType type, const char* serial,
+                           TransportId transport_id, int reply_fd) {
     if (!strcmp(service, "list-forward")) {
         // Create the list of forward redirections.
         std::string listeners = format_listeners();
@@ -991,7 +997,8 @@
         }
 
         std::string error_msg;
-        atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
+        atransport* transport =
+            acquire_one_transport(type, serial, transport_id, nullptr, &error_msg);
         if (!transport) {
             SendFail(reply_fd, error_msg);
             return 1;
@@ -1049,8 +1056,8 @@
     return 0;
 }
 
-int handle_host_request(const char* service, TransportType type,
-                        const char* serial, int reply_fd, asocket* s) {
+int handle_host_request(const char* service, TransportType type, const char* serial,
+                        TransportId transport_id, int reply_fd, asocket* s) {
     if (strcmp(service, "kill") == 0) {
         fprintf(stderr, "adb server killed by remote request\n");
         fflush(stdout);
@@ -1070,7 +1077,14 @@
     if (!strncmp(service, "transport", strlen("transport"))) {
         TransportType type = kTransportAny;
 
-        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+        if (!strncmp(service, "transport-id:", strlen("transport-id:"))) {
+            service += strlen("transport-id:");
+            transport_id = strtoll(service, const_cast<char**>(&service), 10);
+            if (*service != '\0') {
+                SendFail(reply_fd, "invalid transport id");
+                return 1;
+            }
+        } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
             type = kTransportUsb;
         } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
             type = kTransportLocal;
@@ -1082,7 +1096,7 @@
         }
 
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
             s->transport = t;
             SendOkay(reply_fd);
@@ -1125,7 +1139,7 @@
 
     if (!strcmp(service, "features")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
             SendOkay(reply_fd, FeatureSetToString(t->features()));
         } else {
@@ -1178,7 +1192,7 @@
     // These always report "unknown" rather than the actual error, for scripts.
     if (!strcmp(service, "get-serialno")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
         } else {
@@ -1187,7 +1201,7 @@
     }
     if (!strcmp(service, "get-devpath")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
         } else {
@@ -1196,7 +1210,7 @@
     }
     if (!strcmp(service, "get-state")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             return SendOkay(reply_fd, t->connection_state_name());
         } else {
@@ -1214,7 +1228,7 @@
 
     if (!strcmp(service, "reconnect")) {
         std::string response;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &response, true);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
         if (t != nullptr) {
             kick_transport(t);
             response =
@@ -1223,7 +1237,7 @@
         return SendOkay(reply_fd, response);
     }
 
-    int ret = handle_forward_request(service, type, serial, reply_fd);
+    int ret = handle_forward_request(service, type, serial, transport_id, reply_fd);
     if (ret >= 0)
       return ret - 1;
     return -1;
diff --git a/adb/adb.h b/adb/adb.h
index d6b2b81..a4d233e 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -31,8 +31,7 @@
 #include "usb.h"
 
 constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
-constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
-constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
+constexpr size_t MAX_PAYLOAD = 1024 * 1024;
 
 constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
 
@@ -56,6 +55,7 @@
 // Increment this when we want to force users to start a new adb server.
 #define ADB_SERVER_VERSION 39
 
+using TransportId = uint64_t;
 class atransport;
 
 struct amessage {
@@ -149,7 +149,7 @@
 
 int service_to_fd(const char* name, const atransport* transport);
 #if ADB_HOST
-asocket *host_service_to_socket(const char*  name, const char *serial);
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
 #endif
 
 #if !ADB_HOST
@@ -159,7 +159,8 @@
 int       create_jdwp_connection_fd(int  jdwp_pid);
 #endif
 
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
+int handle_forward_request(const char* service, TransportType type, const char* serial,
+                           TransportId transport_id, int reply_fd);
 
 #if !ADB_HOST
 void framebuffer_service(int fd, void *cookie);
@@ -216,15 +217,13 @@
 #define USB_FFS_ADB_IN    USB_FFS_ADB_EP(ep2)
 #endif
 
-int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
+int handle_host_request(const char* service, TransportType type, const char* serial,
+                        TransportId transport_id, int reply_fd, asocket* s);
 
 void handle_online(atransport *t);
 void handle_offline(atransport *t);
 
 void send_connect(atransport *t);
-#if ADB_HOST
-void SendConnectOnHost(atransport* t);
-#endif
 
 void parse_banner(const std::string&, atransport* t);
 
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index e533a00..849a6e7 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -20,6 +20,7 @@
 #include "adb_client.h"
 
 #include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -46,12 +47,20 @@
 
 static TransportType __adb_transport = kTransportAny;
 static const char* __adb_serial = NULL;
+static TransportId __adb_transport_id = 0;
 
 static const char* __adb_server_socket_spec;
 
-void adb_set_transport(TransportType type, const char* serial) {
+void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) {
     __adb_transport = type;
     __adb_serial = serial;
+    __adb_transport_id = transport_id;
+}
+
+void adb_get_transport(TransportType* type, const char** serial, TransportId* transport_id) {
+    if (type) *type = __adb_transport;
+    if (serial) *serial = __adb_serial;
+    if (transport_id) *transport_id = __adb_transport_id;
 }
 
 void adb_set_socket_spec(const char* socket_spec) {
@@ -63,7 +72,10 @@
 
 static int switch_socket_transport(int fd, std::string* error) {
     std::string service;
-    if (__adb_serial) {
+    if (__adb_transport_id) {
+        service += "host:transport-id:";
+        service += std::to_string(__adb_transport_id);
+    } else if (__adb_serial) {
         service += "host:transport:";
         service += __adb_serial;
     } else {
@@ -292,15 +304,18 @@
     return true;
 }
 
-std::string format_host_command(const char* command, TransportType type, const char* serial) {
-    if (serial) {
-        return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+std::string format_host_command(const char* command) {
+    if (__adb_transport_id) {
+        return android::base::StringPrintf("host-transport-id:%" PRIu64 ":%s", __adb_transport_id,
+                                           command);
+    } else if (__adb_serial) {
+        return android::base::StringPrintf("host-serial:%s:%s", __adb_serial, command);
     }
 
     const char* prefix = "host";
-    if (type == kTransportUsb) {
+    if (__adb_transport == kTransportUsb) {
         prefix = "host-usb";
-    } else if (type == kTransportLocal) {
+    } else if (__adb_transport == kTransportLocal) {
         prefix = "host-local";
     }
     return android::base::StringPrintf("%s:%s", prefix, command);
@@ -308,7 +323,7 @@
 
 bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
     std::string result;
-    if (adb_query(format_host_command("features", __adb_transport, __adb_serial), &result, error)) {
+    if (adb_query(format_host_command("features"), &result, error)) {
         *feature_set = StringToFeatureSet(result);
         return true;
     }
diff --git a/adb/adb_client.h b/adb/adb_client.h
index fabec00..fca435e 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -40,7 +40,9 @@
                std::string* _Nonnull error);
 
 // Set the preferred transport to connect to.
-void adb_set_transport(TransportType type, const char* _Nullable serial);
+void adb_set_transport(TransportType type, const char* _Nullable serial, TransportId transport_id);
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial,
+                       TransportId* _Nullable transport_id);
 
 // Set the socket specification for the adb server.
 // This function can only be called once, and the argument must live to the end of the process.
@@ -57,8 +59,7 @@
 bool adb_status(int fd, std::string* _Nonnull error);
 
 // Create a host command corresponding to selected transport type/serial.
-std::string format_host_command(const char* _Nonnull command, TransportType type,
-                                const char* _Nullable serial);
+std::string format_host_command(const char* _Nonnull command);
 
 // Get the feature set of the current preferred transport.
 bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 6f2403d..b236fb3 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -157,7 +157,12 @@
 }
 
 std::string dump_hex(const void* data, size_t byte_count) {
-    byte_count = std::min(byte_count, size_t(16));
+    size_t truncate_len = 16;
+    bool truncated = false;
+    if (byte_count > truncate_len) {
+        byte_count = truncate_len;
+        truncated = true;
+    }
 
     const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
 
@@ -172,6 +177,10 @@
         line.push_back(isprint(ch) ? ch : '.');
     }
 
+    if (truncated) {
+        line += " [truncated]";
+    }
+
     return line;
 }
 
@@ -278,3 +287,29 @@
     fprintf(stderr, "\n");
     return 1;
 }
+
+std::string GetLogFilePath() {
+#if defined(_WIN32)
+    const char log_name[] = "adb.log";
+    WCHAR temp_path[MAX_PATH];
+
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+    DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
+    if (nchars >= arraysize(temp_path) || nchars == 0) {
+        // If string truncation or some other error.
+        fatal("cannot retrieve temporary file path: %s\n",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
+
+    std::string temp_path_utf8;
+    if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
+        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+    }
+
+    return temp_path_utf8 + log_name;
+#else
+    const char* tmp_dir = getenv("TMPDIR");
+    if (tmp_dir == nullptr) tmp_dir = "/tmp";
+    return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
+#endif
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 11c0ec9..f764a0e 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -89,4 +89,6 @@
     }
 };
 
+std::string GetLogFilePath();
+
 #endif
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 372a3b4..abef86a 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -102,7 +102,7 @@
             std::vector<const char*> srcs{src_file_.c_str()};
             SetLineMessage("pulling");
             status_ =
-                br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
+                br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;
             if (status_ != 0) {
                 fprintf(stderr,
                         "Bug report finished but could not be copied to '%s'.\n"
@@ -195,13 +195,13 @@
     DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
 };
 
-int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
+int Bugreport::DoIt(int argc, const char** argv) {
     if (argc > 2) return syntax_error("adb bugreport [PATH]");
 
     // Gets bugreportz version.
     std::string bugz_stdout, bugz_stderr;
     DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
-    int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
+    int status = SendShellCommand("bugreportz -v", false, &version_callback);
     std::string bugz_version = android::base::Trim(bugz_stderr);
     std::string bugz_output = android::base::Trim(bugz_stdout);
 
@@ -214,7 +214,7 @@
             fprintf(stderr,
                     "Failed to get bugreportz version, which is only available on devices "
                     "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
-            return SendShellCommand(transport_type, serial, "bugreport", false);
+            return SendShellCommand("bugreport", false);
         }
 
         // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
@@ -265,7 +265,7 @@
         bugz_command = "bugreportz";
     }
     BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
-    return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
+    return SendShellCommand(bugz_command, false, &bugz_callback);
 }
 
 void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
@@ -274,10 +274,9 @@
         LinePrinter::INFO);
 }
 
-int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
-                                const std::string& command, bool disable_shell_protocol,
+int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol,
                                 StandardStreamsCallbackInterface* callback) {
-    return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
+    return send_shell_command(command, disable_shell_protocol, callback);
 }
 
 bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport.h b/adb/bugreport.h
index d9a4468..413439b 100644
--- a/adb/bugreport.h
+++ b/adb/bugreport.h
@@ -29,14 +29,13 @@
   public:
     Bugreport() : line_printer_() {
     }
-    int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv);
+    int DoIt(int argc, const char** argv);
 
   protected:
     // Functions below are abstractions of external functions so they can be
     // mocked on tests.
     virtual int SendShellCommand(
-        TransportType transport_type, const char* serial, const std::string& command,
-        bool disable_shell_protocol,
+        const std::string& command, bool disable_shell_protocol,
         StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
 
     virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index d3787b4..72ca59a 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -51,8 +51,8 @@
 // Empty functions so tests don't need to be linked against commandline.cpp
 DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
 
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
-                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+                       StandardStreamsCallbackInterface* callback) {
     ADD_FAILURE() << "send_shell_command() should have been mocked";
     return -42;
 }
@@ -62,7 +62,7 @@
     kStreamStderr,
 };
 
-// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher
+// gmock black magic to provide a WithArg<2>(WriteOnStdout(output)) matcher
 typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
 
 class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
@@ -118,9 +118,8 @@
 
 class BugreportMock : public Bugreport {
   public:
-    MOCK_METHOD5(SendShellCommand,
-                 int(TransportType transport_type, const char* serial, const std::string& command,
-                     bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
+    MOCK_METHOD3(SendShellCommand, int(const std::string& command, bool disable_shell_protocol,
+                                       StandardStreamsCallbackInterface* callback));
     MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
                                   bool copy_attrs, const char* name));
     MOCK_METHOD2(UpdateProgress, void(const std::string&, int));
@@ -136,10 +135,9 @@
     }
 
     void ExpectBugreportzVersion(const std::string& version) {
-        EXPECT_CALL(br_,
-                    SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
-            .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
-                            WithArg<4>(ReturnCallbackDone(0))));
+        EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+            .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())),
+                            WithArg<2>(ReturnCallbackDone(0))));
     }
 
     void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") {
@@ -153,26 +151,26 @@
 // Tests when called with invalid number of arguments
 TEST_F(BugreportTest, InvalidNumberArgs) {
     const char* args[] = {"bugreport", "to", "principal"};
-    ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
+    ASSERT_EQ(1, br_.DoIt(3, args));
 }
 
 // Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
 // to the flat-file format ('bugreport' binary on device)
 TEST_F(BugreportTest, NoArgumentsPreNDevice) {
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStderr("")),
                         // Write some bogus output on stdout to make sure it's ignored
-                        WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
-                        WithArg<4>(ReturnCallbackDone(0))));
+                        WithArg<2>(WriteOnStdout("Dude, where is my bugreportz?")),
+                        WithArg<2>(ReturnCallbackDone(0))));
     // clang-format on
     std::string bugreport = "Reported the bug was.";
     CaptureStdout();
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
+    EXPECT_CALL(br_, SendShellCommand("bugreport", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout(bugreport)), Return(0)));
 
     const char* args[] = {"bugreport"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+    ASSERT_EQ(0, br_.DoIt(1, args));
     ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
 }
 
@@ -183,15 +181,15 @@
 
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+    ASSERT_EQ(0, br_.DoIt(1, args));
 }
 
 // Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
@@ -201,47 +199,47 @@
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
     ExpectProgress(50, "da_bugreport.zip");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
-                        WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
-                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")),
+                        WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+    ASSERT_EQ(0, br_.DoIt(1, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
 TEST_F(BugreportTest, OkNDevice) {
     ExpectBugreportzVersion("1.0");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds but response was sent in
 // multiple buffer writers and without progress updates.
 TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
     ExpectBugreportzVersion("1.0");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
-                        WithArg<4>(WriteOnStdout("/bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device")),
+                        WithArg<2>(WriteOnStdout("/bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and displays progress.
@@ -252,32 +250,32 @@
     ExpectProgress(50);
     ExpectProgress(99);
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
             // Name might change on OK, so make sure the right one is picked.
-            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
+            WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
             // Progress line in one write
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")),
             // Add some bogus lines
-            WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
+            WithArg<2>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
             // Multiple progress lines in one write
-            WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
             // Progress line in multiple writes
-            WithArg<4>(WriteOnStdout("PROG")),
-            WithArg<4>(WriteOnStdout("RESS:99")),
-            WithArg<4>(WriteOnStdout("/100\n")),
+            WithArg<2>(WriteOnStdout("PROG")),
+            WithArg<2>(WriteOnStdout("RESS:99")),
+            WithArg<2>(WriteOnStdout("/100\n")),
             // Split last message as well, just in case
-            WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
-            WithArg<4>(WriteOnStdout(".zip")),
-            WithArg<4>(ReturnCallbackDone())));
+            WithArg<2>(WriteOnStdout("OK:/device/bugreport")),
+            WithArg<2>(WriteOnStdout(".zip")),
+            WithArg<2>(ReturnCallbackDone())));
     // clang-format on
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
@@ -287,28 +285,28 @@
     ExpectProgress(50);
     ExpectProgress(75);
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
-            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
-            WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
+            WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+            WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
             // 25% should be ignored becaused it receded.
-            WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
-            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+            WithArg<2>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
+            WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
             // 75% should be ignored becaused it didn't change.
-            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+            WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
             // Try a receeding percentage with a different max progress
-            WithArg<4>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
-            WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-            WithArg<4>(ReturnCallbackDone())));
+            WithArg<2>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
+            WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+            WithArg<2>(ReturnCallbackDone())));
     // clang-format on
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
@@ -317,21 +315,21 @@
     ExpectProgress(0);
     ExpectProgress(1);
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
-            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100000\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
-            WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-            WithArg<4>(ReturnCallbackDone())));
+            WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100000\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+            WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+            WithArg<2>(ReturnCallbackDone())));
     // clang-format on
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport dir' when it succeeds and destination is a directory.
@@ -341,30 +339,30 @@
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
 
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
-                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", td.path};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file' when it succeeds
 TEST_F(BugreportTest, OkNoExtension) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip\n")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
@@ -374,28 +372,28 @@
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
 
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
-                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", td.path};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreport itself failed
 TEST_F(BugreportTest, BugreportzReturnedFail) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         .WillOnce(
-            DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
+            DoAll(WithArg<2>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<2>(ReturnCallbackDone())));
 
     CaptureStderr();
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
     ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
 }
 
@@ -404,13 +402,13 @@
 // multiple buffer writes
 TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("FAIL")), WithArg<2>(WriteOnStdout(":D'OH!\n")),
+                        WithArg<2>(ReturnCallbackDone())));
 
     CaptureStderr();
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
     ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
 }
 
@@ -418,23 +416,22 @@
 // response.
 TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("bugreportz? What am I, a zombie?")),
+                        WithArg<2>(ReturnCallbackDone())));
 
     CaptureStderr();
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
     ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreportz -v command failed
 TEST_F(BugreportTest, BugreportzVersionFailed) {
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
-        .WillOnce(Return(666));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)).WillOnce(Return(666));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(666, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
@@ -442,29 +439,28 @@
     ExpectBugreportzVersion("");
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the main bugreportz command failed
 TEST_F(BugreportTest, BugreportzFailed) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(Return(666));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)).WillOnce(Return(666));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(666, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreport could not be pulled
 TEST_F(BugreportTest, PullFails) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, HasSubstr("file.zip")))
+                                false, HasSubstr("file.zip")))
         .WillOnce(Return(false));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(1, br_.DoIt(2, args));
 }
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 62798cd..f0d0ce7 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -39,33 +39,7 @@
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
-static std::string GetLogFilePath() {
-#if defined(_WIN32)
-    const char log_name[] = "adb.log";
-    WCHAR temp_path[MAX_PATH];
-
-    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
-    DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
-    if (nchars >= arraysize(temp_path) || nchars == 0) {
-        // If string truncation or some other error.
-        fatal("cannot retrieve temporary file path: %s\n",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    }
-
-    std::string temp_path_utf8;
-    if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
-        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
-    }
-
-    return temp_path_utf8 + log_name;
-#else
-    const char* tmp_dir = getenv("TMPDIR");
-    if (tmp_dir == nullptr) tmp_dir = "/tmp";
-    return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
-#endif
-}
-
-static void setup_daemon_logging(void) {
+static void setup_daemon_logging() {
     const std::string log_file_path(GetLogFilePath());
     int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
     if (fd == -1) {
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index e7f44c6..8120199 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -333,6 +333,13 @@
             return;
         }
 
+        rc = libusb_set_interface_alt_setting(handle.get(), interface_num, 0);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to set interface alt setting for device '" << device_serial
+                         << "'" << libusb_error_name(rc);
+            return;
+        }
+
         for (uint8_t endpoint : {bulk_in, bulk_out}) {
             rc = libusb_clear_halt(handle.get(), endpoint);
             if (rc != 0) {
@@ -412,8 +419,13 @@
     if (it != usb_handles.end()) {
         if (!it->second->device_handle) {
             // If the handle is null, we were never able to open the device.
-            unregister_usb_transport(it->second.get());
+
+            // Temporarily release the usb handles mutex to avoid deadlock.
+            std::unique_ptr<usb_handle> handle = std::move(it->second);
             usb_handles.erase(it);
+            lock.unlock();
+            unregister_usb_transport(handle.get());
+            lock.lock();
         } else {
             // Closure of the transport will erase the usb_handle.
         }
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index c9f1ee9..d126f52 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -62,11 +62,11 @@
 #include "shell_service.h"
 #include "sysdeps/chrono.h"
 
-static int install_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app(int argc, const char** argv);
+static int install_multiple_app(int argc, const char** argv);
+static int uninstall_app(int argc, const char** argv);
+static int install_app_legacy(int argc, const char** argv);
+static int uninstall_app_legacy(int argc, const char** argv);
 
 extern int gListenAll;
 
@@ -90,6 +90,7 @@
         " -d         use USB device (error if multiple devices connected)\n"
         " -e         use TCP/IP device (error if multiple TCP/IP devices available)\n"
         " -s SERIAL  use device with given serial (overrides $ANDROID_SERIAL)\n"
+        " -t ID      use device with given transport id\n"
         " -H         name of adb server host [default=localhost]\n"
         " -P         port of adb server [default=5037]\n"
         " -L SOCKET  listen on given socket for adb server [default=tcp:localhost:5037]\n"
@@ -685,6 +686,10 @@
 
     // Parse shell-specific command-line options.
     argv[0] = "adb shell"; // So getopt(3) error messages start "adb shell".
+#ifdef _WIN32
+    // fixes "adb shell -l" crash on Windows, b/37284906
+    __argv = const_cast<char**>(argv);
+#endif
     optind = 1; // argv[0] is always "shell", so set `optind` appropriately.
     int opt;
     while ((opt = getopt(argc, const_cast<char**>(argv), "+e:ntTx")) != -1) {
@@ -986,13 +991,16 @@
 #endif /* !defined(_WIN32) */
 }
 
-static bool wait_for_device(const char* service, TransportType t, const char* serial) {
+static bool wait_for_device(const char* service) {
     std::vector<std::string> components = android::base::Split(service, "-");
     if (components.size() < 3 || components.size() > 4) {
         fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
         return false;
     }
 
+    TransportType t;
+    adb_get_transport(&t, nullptr, nullptr);
+
     // Was the caller vague about what they'd like us to wait for?
     // If so, check they weren't more specific in their choice of transport type.
     if (components.size() == 3) {
@@ -1019,7 +1027,7 @@
         return false;
     }
 
-    std::string cmd = format_host_command(android::base::Join(components, "-").c_str(), t, serial);
+    std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
     return adb_command(cmd);
 }
 
@@ -1065,8 +1073,8 @@
     return true;
 }
 
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
-                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+                       StandardStreamsCallbackInterface* callback) {
     int fd;
     bool use_shell_protocol = false;
 
@@ -1097,7 +1105,7 @@
         }
 
         fprintf(stderr, "- waiting for device -\n");
-        if (!wait_for_device("wait-for-device", transport_type, serial)) {
+        if (!wait_for_device("wait-for-device")) {
             return 1;
         }
     }
@@ -1111,7 +1119,7 @@
     return exit_code;
 }
 
-static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
+static int logcat(int argc, const char** argv) {
     char* log_tags = getenv("ANDROID_LOG_TAGS");
     std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
 
@@ -1128,7 +1136,7 @@
     }
 
     // No need for shell protocol with logcat, always disable for simplicity.
-    return send_shell_command(transport, serial, cmd, true);
+    return send_shell_command(cmd, true);
 }
 
 static void write_zeros(int bytes, int fd) {
@@ -1340,6 +1348,7 @@
 
     // We need to check for -d and -e before we look at $ANDROID_SERIAL.
     const char* serial = nullptr;
+    TransportId transport_id = 0;
 
     while (argc > 0) {
         if (!strcmp(argv[0],"server")) {
@@ -1359,7 +1368,7 @@
                 fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
                 return 1;
             }
-        } else if (argv[0][0]=='-' && argv[0][1]=='s') {
+        } else if (!strncmp(argv[0], "-s", 2)) {
             if (isdigit(argv[0][2])) {
                 serial = argv[0] + 2;
             } else {
@@ -1368,6 +1377,19 @@
                 argc--;
                 argv++;
             }
+        } else if (!strncmp(argv[0], "-t", 2)) {
+            const char* id;
+            if (isdigit(argv[0][2])) {
+                id = argv[0] + 2;
+            } else {
+                id = argv[1];
+                argc--;
+                argv++;
+            }
+            transport_id = strtoll(id, const_cast<char**>(&id), 10);
+            if (*id != '\0') {
+                return syntax_error("invalid transport id");
+            }
         } else if (!strcmp(argv[0],"-d")) {
             transport_type = kTransportUsb;
         } else if (!strcmp(argv[0],"-e")) {
@@ -1451,7 +1473,7 @@
         serial = getenv("ANDROID_SERIAL");
     }
 
-    adb_set_transport(transport_type, serial);
+    adb_set_transport(transport_type, serial, transport_id);
 
     if (is_server) {
         if (no_daemon || is_daemon) {
@@ -1478,7 +1500,7 @@
     if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
         const char* service = argv[0];
 
-        if (!wait_for_device(service, transport_type, serial)) {
+        if (!wait_for_device(service)) {
             return 1;
         }
 
@@ -1566,9 +1588,13 @@
         } else {
             return 0;
         }
-    }
-    else if (!strcmp(argv[0], "tcpip") && argc > 1) {
-        return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
+    } else if (!strcmp(argv[0], "tcpip")) {
+        if (argc != 2) return syntax_error("tcpip requires an argument");
+        int port;
+        if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
+            return syntax_error("tcpip: invalid port: %s", argv[1]);
+        }
+        return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
     }
     else if (!strcmp(argv[0], "remount") ||
              !strcmp(argv[0], "reboot") ||
@@ -1589,7 +1615,7 @@
         return adb_root(argv[0]) ? 0 : 1;
     } else if (!strcmp(argv[0], "bugreport")) {
         Bugreport bugreport;
-        return bugreport.DoIt(transport_type, serial, argc, argv);
+        return bugreport.DoIt(argc, argv);
     } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
         bool reverse = !strcmp(argv[0], "reverse");
         ++argv;
@@ -1685,20 +1711,20 @@
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return syntax_error("install requires an argument");
         if (_use_legacy_install()) {
-            return install_app_legacy(transport_type, serial, argc, argv);
+            return install_app_legacy(argc, argv);
         }
-        return install_app(transport_type, serial, argc, argv);
+        return install_app(argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) return syntax_error("install-multiple requires an argument");
-        return install_multiple_app(transport_type, serial, argc, argv);
+        return install_multiple_app(argc, argv);
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return syntax_error("uninstall requires an argument");
         if (_use_legacy_install()) {
-            return uninstall_app_legacy(transport_type, serial, argc, argv);
+            return uninstall_app_legacy(argc, argv);
         }
-        return uninstall_app(transport_type, serial, argc, argv);
+        return uninstall_app(argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
         std::string src;
@@ -1752,11 +1778,11 @@
         !strcmp(argv[0],"get-serialno") ||
         !strcmp(argv[0],"get-devpath"))
     {
-        return adb_query_command(format_host_command(argv[0], transport_type, serial));
+        return adb_query_command(format_host_command(argv[0]));
     }
     /* other commands */
     else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
-        return logcat(transport_type, serial, argc, argv);
+        return logcat(argc, argv);
     }
     else if (!strcmp(argv[0],"ppp")) {
         return ppp(argc, argv);
@@ -1819,7 +1845,7 @@
         return adb_query_command("host:host-features");
     } else if (!strcmp(argv[0], "reconnect")) {
         if (argc == 1) {
-            return adb_query_command(format_host_command(argv[0], transport_type, serial));
+            return adb_query_command(format_host_command(argv[0]));
         } else if (argc == 2) {
             if (!strcmp(argv[1], "device")) {
                 std::string err;
@@ -1838,7 +1864,7 @@
     return 1;
 }
 
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+static int uninstall_app(int argc, const char** argv) {
     // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
     std::string cmd = "cmd package";
     while (argc-- > 0) {
@@ -1854,10 +1880,10 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(transport, serial, cmd, false);
+    return send_shell_command(cmd, false);
 }
 
-static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
+static int install_app(int argc, const char** argv) {
     // The last argument must be the APK file
     const char* file = argv[argc - 1];
     if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
@@ -1910,9 +1936,7 @@
     return 1;
 }
 
-static int install_multiple_app(TransportType transport, const char* serial, int argc,
-                                const char** argv)
-{
+static int install_multiple_app(int argc, const char** argv) {
     // Find all APK arguments starting at end.
     // All other arguments passed through verbatim.
     int first_apk = -1;
@@ -2037,17 +2061,17 @@
     return EXIT_FAILURE;
 }
 
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+static int pm_command(int argc, const char** argv) {
     std::string cmd = "pm";
 
     while (argc-- > 0) {
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(transport, serial, cmd, false);
+    return send_shell_command(cmd, false);
 }
 
-static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+static int uninstall_app_legacy(int argc, const char** argv) {
     /* if the user choose the -k option, we refuse to do it until devices are
        out with the option to uninstall the remaining data somehow (adb/ui) */
     int i;
@@ -2063,15 +2087,15 @@
     }
 
     /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
-    return pm_command(transport, serial, argc, argv);
+    return pm_command(argc, argv);
 }
 
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+static int delete_file(const std::string& filename) {
     std::string cmd = "rm -f " + escape_arg(filename);
-    return send_shell_command(transport, serial, cmd, false);
+    return send_shell_command(cmd, false);
 }
 
-static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+static int install_app_legacy(int argc, const char** argv) {
     static const char *const DATA_DEST = "/data/local/tmp/%s";
     static const char *const SD_DEST = "/sdcard/tmp/%s";
     const char* where = DATA_DEST;
@@ -2100,9 +2124,9 @@
         where, android::base::Basename(argv[last_apk]).c_str());
     if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
     argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
-    result = pm_command(transport, serial, argc, argv);
+    result = pm_command(argc, argv);
 
 cleanup_apk:
-    delete_file(transport, serial, apk_dest);
+    delete_file(apk_dest);
     return result;
 }
diff --git a/adb/commandline.h b/adb/commandline.h
index 9ba69a3..36cd798 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -91,8 +91,8 @@
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
 // if |callback| is non-null, stdout/stderr output will be handled by it.
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
-                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback =
-                                                        &DEFAULT_STANDARD_STREAMS_CALLBACK);
+int send_shell_command(
+    const std::string& command, bool disable_shell_protocol,
+    StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
 
 #endif  // COMMANDLINE_H
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 0f92282..87ed3db 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -49,16 +50,11 @@
 #define MAX_PACKET_SIZE_HS 512
 #define MAX_PACKET_SIZE_SS 1024
 
-// Kernels before 3.3 have a 16KiB transfer limit  That limit was replaced
-// with a 16MiB global limit in 3.3, but each URB submitted required a
-// contiguous kernel allocation, so you would get ENOMEM if you tried to
-// send something larger than the biggest available contiguous kernel
-// memory region. Large contiguous allocations could be unreliable
-// on a device kernel that has been running for a while fragmenting its
-// memory so we start with a larger allocation, and shrink the amount if
-// necessary.
 #define USB_FFS_BULK_SIZE 16384
 
+// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
+#define USB_FFS_NUM_BUFS ((MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+
 #define cpu_to_le16(x) htole16(x)
 #define cpu_to_le32(x) htole32(x)
 
@@ -234,6 +230,26 @@
     },
 };
 
+static void aio_block_init(aio_block* aiob) {
+    aiob->iocb.resize(USB_FFS_NUM_BUFS);
+    aiob->iocbs.resize(USB_FFS_NUM_BUFS);
+    aiob->events.resize(USB_FFS_NUM_BUFS);
+    aiob->num_submitted = 0;
+    for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
+        aiob->iocbs[i] = &aiob->iocb[i];
+    }
+}
+
+static int getMaxPacketSize(int ffs_fd) {
+    usb_endpoint_descriptor desc;
+    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+        D("[ could not get endpoint descriptor! (%d) ]", errno);
+        return MAX_PACKET_SIZE_HS;
+    } else {
+        return desc.wMaxPacketSize;
+    }
+}
+
 bool init_functionfs(struct usb_handle* h) {
     LOG(INFO) << "initializing functionfs";
 
@@ -301,6 +317,14 @@
         goto err;
     }
 
+    if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
+        io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
+        D("[ aio: got error on io_setup (%d) ]", errno);
+    }
+
+    h->read_aiob.fd = h->bulk_out;
+    h->write_aiob.fd = h->bulk_in;
+
     h->max_rw = MAX_PAYLOAD;
     while (h->max_rw >= USB_FFS_BULK_SIZE && retries < ENDPOINT_ALLOC_RETRIES) {
         int ret_in = ioctl(h->bulk_in, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
@@ -410,6 +434,65 @@
     return 0;
 }
 
+static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
+    aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+    bool zero_packet = false;
+
+    int num_bufs = len / USB_FFS_BULK_SIZE + (len % USB_FFS_BULK_SIZE == 0 ? 0 : 1);
+    const char* cur_data = reinterpret_cast<const char*>(data);
+    int packet_size = getMaxPacketSize(aiob->fd);
+
+    if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
+        0) {
+        D("[ Failed to madvise: %d ]", errno);
+    }
+
+    for (int i = 0; i < num_bufs; i++) {
+        int buf_len = std::min(len, USB_FFS_BULK_SIZE);
+        io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
+
+        len -= buf_len;
+        cur_data += buf_len;
+
+        if (len == 0 && buf_len % packet_size == 0 && read) {
+            // adb does not expect the device to send a zero packet after data transfer,
+            // but the host *does* send a zero packet for the device to read.
+            zero_packet = true;
+        }
+    }
+    if (zero_packet) {
+        io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
+                packet_size, 0, read);
+        num_bufs += 1;
+    }
+
+    if (io_submit(aiob->ctx, num_bufs, aiob->iocbs.data()) < num_bufs) {
+        D("[ aio: got error submitting %s (%d) ]", read ? "read" : "write", errno);
+        return -1;
+    }
+    if (TEMP_FAILURE_RETRY(
+            io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(), nullptr)) < num_bufs) {
+        D("[ aio: got error waiting %s (%d) ]", read ? "read" : "write", errno);
+        return -1;
+    }
+    for (int i = 0; i < num_bufs; i++) {
+        if (aiob->events[i].res < 0) {
+            errno = aiob->events[i].res;
+            D("[ aio: got error event on %s (%d) ]", read ? "read" : "write", errno);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, true);
+}
+
+static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, false);
+}
+
 static void usb_ffs_kick(usb_handle* h) {
     int err;
 
@@ -438,6 +521,9 @@
     h->kicked = false;
     adb_close(h->bulk_out);
     adb_close(h->bulk_in);
+    io_destroy(h->read_aiob.ctx);
+    io_destroy(h->write_aiob.ctx);
+
     // Notify usb_adb_open_thread to open a new connection.
     h->lock.lock();
     h->open_new_connection = true;
@@ -450,8 +536,17 @@
 
     usb_handle* h = new usb_handle();
 
-    h->write = usb_ffs_write;
-    h->read = usb_ffs_read;
+    if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+        // Devices on older kernels (< 3.18) will not have aio support for ffs
+        // unless backported. Fall back on the non-aio functions instead.
+        h->write = usb_ffs_write;
+        h->read = usb_ffs_read;
+    } else {
+        h->write = usb_ffs_aio_write;
+        h->read = usb_ffs_aio_read;
+        aio_block_init(&h->read_aiob);
+        aio_block_init(&h->write_aiob);
+    }
     h->kick = usb_ffs_kick;
     h->close = usb_ffs_close;
 
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
index 55b5995..db1a6d6 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/usb.h
@@ -20,6 +20,17 @@
 #include <condition_variable>
 #include <mutex>
 
+#include <asyncio/AsyncIO.h>
+
+struct aio_block {
+    std::vector<struct iocb> iocb;
+    std::vector<struct iocb*> iocbs;
+    std::vector<struct io_event> events;
+    aio_context_t ctx;
+    int num_submitted;
+    int fd;
+};
+
 struct usb_handle {
     usb_handle() : kicked(false) {
     }
@@ -39,7 +50,11 @@
     int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
     int bulk_in = -1;  /* "in" from the host's perspective => sink for adbd */
 
+    // Access to these blocks is very not thread safe. Have one block for both the
+    // read and write threads.
+    struct aio_block read_aiob;
+    struct aio_block write_aiob;
+
     int max_rw;
 };
 
-bool init_functionfs(struct usb_handle* h);
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 2576fb1..26f8d83 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -441,7 +441,7 @@
         syncsendbuf sbuf;
         sbuf.id = ID_DATA;
         while (true) {
-            int bytes_read = adb_read(lfd, sbuf.data, max);
+            int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
             if (bytes_read == -1) {
                 Error("reading '%s' locally failed: %s", lpath, strerror(errno));
                 adb_close(lfd);
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 2acf661..c6f3e66 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -206,6 +206,12 @@
     __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
 
     int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) <
+        0) {
+        D("[ Failed to fadvise: %d ]", errno);
+    }
+
     if (fd < 0 && errno == ENOENT) {
         if (!secure_mkdirs(android::base::Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
@@ -283,25 +289,25 @@
     // reading and throwing away ID_DATA packets until the other side notices
     // that we've reported an error.
     while (true) {
-        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) break;
 
         if (msg.data.id == ID_DONE) {
-            goto abort;
+            break;
         } else if (msg.data.id != ID_DATA) {
             char id[5];
             memcpy(id, &msg.data.id, sizeof(msg.data.id));
             id[4] = '\0';
             D("handle_send_fail received unexpected id '%s' during failure", id);
-            goto abort;
+            break;
         }
 
         if (msg.data.size > buffer.size()) {
             D("handle_send_fail received oversized packet of length '%u' during failure",
               msg.data.size);
-            goto abort;
+            break;
         }
 
-        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) break;
     }
 
 abort:
@@ -413,10 +419,14 @@
         return false;
     }
 
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
+        D("[ Failed to fadvise: %d ]", errno);
+    }
+
     syncmsg msg;
     msg.data.id = ID_DATA;
     while (true) {
-        int r = adb_read(fd, &buffer[0], buffer.size());
+        int r = adb_read(fd, &buffer[0], buffer.size() - sizeof(msg.data));
         if (r <= 0) {
             if (r == 0) break;
             SendSyncFailErrno(s, "read failed");
diff --git a/adb/services.cpp b/adb/services.cpp
index 9605e6e..aff7012 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -58,6 +58,7 @@
 #include "transport.h"
 
 struct stinfo {
+    const char* service_name;
     void (*func)(int fd, void *cookie);
     int fd;
     void *cookie;
@@ -65,7 +66,7 @@
 
 static void service_bootstrap_func(void* x) {
     stinfo* sti = reinterpret_cast<stinfo*>(x);
-    adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
+    adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
     sti->func(sti->fd, sti->cookie);
     free(sti);
 }
@@ -150,6 +151,7 @@
 
     sync();
 
+    if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
     std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
     if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
         WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
@@ -159,8 +161,7 @@
     return true;
 }
 
-void reboot_service(int fd, void* arg)
-{
+void reboot_service(int fd, void* arg) {
     if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
         // Don't return early. Give the reboot command time to take effect
         // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
@@ -187,7 +188,7 @@
         return -1;
     }
     VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
-    if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+    if (handle_forward_request(command, kTransportAny, nullptr, 0, s[1]) < 0) {
         SendFail(s[1], "not a reverse forwarding command");
     }
     adb_close(s[1]);
@@ -235,8 +236,7 @@
 
 #endif  // !ADB_HOST
 
-static int create_service_thread(void (*func)(int, void *), void *cookie)
-{
+static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
     int s[2];
     if (adb_socketpair(s)) {
         printf("cannot create service socket pair\n");
@@ -257,6 +257,7 @@
     if (sti == nullptr) {
         fatal("cannot allocate stinfo");
     }
+    sti->service_name = service_name;
     sti->func = func;
     sti->cookie = cookie;
     sti->fd = s[1];
@@ -280,7 +281,7 @@
     } else if(!strncmp("dev:", name, 4)) {
         ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
     } else if(!strncmp(name, "framebuffer:", 12)) {
-        ret = create_service_thread(framebuffer_service, 0);
+        ret = create_service_thread("fb", framebuffer_service, nullptr);
     } else if (!strncmp(name, "jdwp:", 5)) {
         ret = create_jdwp_connection_fd(atoi(name+5));
     } else if(!strncmp(name, "shell", 5)) {
@@ -288,17 +289,17 @@
     } else if(!strncmp(name, "exec:", 5)) {
         ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if(!strncmp(name, "sync:", 5)) {
-        ret = create_service_thread(file_sync_service, NULL);
+        ret = create_service_thread("sync", file_sync_service, nullptr);
     } else if(!strncmp(name, "remount:", 8)) {
-        ret = create_service_thread(remount_service, NULL);
+        ret = create_service_thread("remount", remount_service, nullptr);
     } else if(!strncmp(name, "reboot:", 7)) {
         void* arg = strdup(name + 7);
         if (arg == NULL) return -1;
-        ret = create_service_thread(reboot_service, arg);
+        ret = create_service_thread("reboot", reboot_service, arg);
     } else if(!strncmp(name, "root:", 5)) {
-        ret = create_service_thread(restart_root_service, NULL);
+        ret = create_service_thread("root", restart_root_service, nullptr);
     } else if(!strncmp(name, "unroot:", 7)) {
-        ret = create_service_thread(restart_unroot_service, NULL);
+        ret = create_service_thread("unroot", restart_unroot_service, nullptr);
     } else if(!strncmp(name, "backup:", 7)) {
         ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
                                                           (name + 7)).c_str(),
@@ -311,17 +312,20 @@
         if (sscanf(name + 6, "%d", &port) != 1) {
             return -1;
         }
-        ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
+        ret = create_service_thread("tcp", restart_tcp_service, reinterpret_cast<void*>(port));
     } else if(!strncmp(name, "usb:", 4)) {
-        ret = create_service_thread(restart_usb_service, NULL);
+        ret = create_service_thread("usb", restart_usb_service, nullptr);
     } else if (!strncmp(name, "reverse:", 8)) {
         ret = reverse_service(name + 8);
     } else if(!strncmp(name, "disable-verity:", 15)) {
-        ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
+        ret = create_service_thread("verity-on", set_verity_enabled_state_service,
+                                    reinterpret_cast<void*>(0));
     } else if(!strncmp(name, "enable-verity:", 15)) {
-        ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
+        ret = create_service_thread("verity-off", set_verity_enabled_state_service,
+                                    reinterpret_cast<void*>(1));
     } else if (!strcmp(name, "reconnect")) {
-        ret = create_service_thread(reconnect_service, const_cast<atransport*>(transport));
+        ret = create_service_thread("reconnect", reconnect_service,
+                                    const_cast<atransport*>(transport));
 #endif
     }
     if (ret >= 0) {
@@ -334,6 +338,7 @@
 struct state_info {
     TransportType transport_type;
     std::string serial;
+    TransportId transport_id;
     ConnectionState state;
 };
 
@@ -346,7 +351,8 @@
         bool is_ambiguous = false;
         std::string error = "unknown error";
         const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
-        atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
+        atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
+                                              &is_ambiguous, &error);
         if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
             SendOkay(fd);
             break;
@@ -437,9 +443,11 @@
 #endif
 
 #if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial) {
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
     if (!strcmp(name,"track-devices")) {
-        return create_device_tracker();
+        return create_device_tracker(false);
+    } else if (!strcmp(name, "track-devices-l")) {
+        return create_device_tracker(true);
     } else if (android::base::StartsWith(name, "wait-for-")) {
         name += strlen("wait-for-");
 
@@ -450,6 +458,7 @@
         }
 
         if (serial) sinfo->serial = serial;
+        sinfo->transport_id = transport_id;
 
         if (android::base::StartsWith(name, "local")) {
             name += strlen("local");
@@ -478,11 +487,17 @@
             return nullptr;
         }
 
-        int fd = create_service_thread(wait_for_state, sinfo.release());
+        int fd = create_service_thread("wait", wait_for_state, sinfo.get());
+        if (fd != -1) {
+            sinfo.release();
+        }
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
         char* host = strdup(name + 8);
-        int fd = create_service_thread(connect_service, host);
+        int fd = create_service_thread("connect", connect_service, host);
+        if (fd == -1) {
+            free(host);
+        }
         return create_local_socket(fd);
     }
     return NULL;
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index 253d14a..49e0363 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -93,21 +93,9 @@
 /* Helper function to get A/B suffix, if any. If the device isn't
  * using A/B the empty string is returned. Otherwise either "_a",
  * "_b", ... is returned.
- *
- * Note that since sometime in O androidboot.slot_suffix is deprecated
- * and androidboot.slot should be used instead. Since bootloaders may
- * be out of sync with the OS, we check both and for extra safety
- * prepend a leading underscore if there isn't one already.
  */
 static std::string get_ab_suffix() {
-    std::string ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
-    if (ab_suffix == "") {
-        ab_suffix = android::base::GetProperty("ro.boot.slot", "");
-    }
-    if (ab_suffix.size() > 0 && ab_suffix[0] != '_') {
-        ab_suffix = std::string("_") + ab_suffix;
-    }
-    return ab_suffix;
+    return android::base::GetProperty("ro.boot.slot_suffix", "");
 }
 
 /* Use AVB to turn verity on/off */
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index ee821f8..0c7e1f9 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -95,6 +95,7 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <private/android_logger.h>
 
@@ -212,6 +213,13 @@
     WaitForExit();
 }
 
+static std::string GetHostName() {
+    char buf[HOST_NAME_MAX];
+    if (gethostname(buf, sizeof(buf)) != -1 && strcmp(buf, "localhost") != 0) return buf;
+
+    return android::base::GetProperty("ro.product.device", "android");
+}
+
 bool Subprocess::ForkAndExec(std::string* error) {
     unique_fd child_stdinout_sfd, child_stderr_sfd;
     unique_fd parent_error_sfd, child_error_sfd;
@@ -250,11 +258,11 @@
     }
 
     if (pw != nullptr) {
-        // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
         env["HOME"] = pw->pw_dir;
+        env["HOSTNAME"] = GetHostName();
         env["LOGNAME"] = pw->pw_name;
-        env["USER"] = pw->pw_name;
         env["SHELL"] = pw->pw_shell;
+        env["USER"] = pw->pw_name;
     }
 
     if (!terminal_type_.empty()) {
@@ -435,8 +443,7 @@
 void Subprocess::ThreadHandler(void* userdata) {
     Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
 
-    adb_thread_setname(android::base::StringPrintf(
-            "shell srvc %d", subprocess->pid()));
+    adb_thread_setname(android::base::StringPrintf("shell svc %d", subprocess->pid()));
 
     D("passing data streams for PID %d", subprocess->pid());
     subprocess->PassDataStreams();
diff --git a/adb/socket.h b/adb/socket.h
index 4acdf4a..64d05a9 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -19,84 +19,83 @@
 
 #include <stddef.h>
 
+#include <memory>
+
 #include "fdevent.h"
 
 struct apacket;
 class atransport;
 
 /* An asocket represents one half of a connection between a local and
-** remote entity.  A local asocket is bound to a file descriptor.  A
-** remote asocket is bound to the protocol engine.
-*/
+ * remote entity.  A local asocket is bound to a file descriptor.  A
+ * remote asocket is bound to the protocol engine.
+ */
 struct asocket {
-        /* chain pointers for the local/remote list of
-        ** asockets that this asocket lives in
-        */
-    asocket *next;
-    asocket *prev;
+    /* chain pointers for the local/remote list of
+     * asockets that this asocket lives in
+     */
+    asocket* next;
+    asocket* prev;
 
-        /* the unique identifier for this asocket
-        */
+    /* the unique identifier for this asocket
+     */
     unsigned id;
 
-        /* flag: set when the socket's peer has closed
-        ** but packets are still queued for delivery
-        */
-    int    closing;
+    /* flag: set when the socket's peer has closed
+     * but packets are still queued for delivery
+     */
+    int closing;
 
     // flag: set when the socket failed to write, so the socket will not wait to
     // write packets and close directly.
     bool has_write_error;
 
-        /* flag: quit adbd when both ends close the
-        ** local service socket
-        */
-    int    exit_on_close;
+    /* flag: quit adbd when both ends close the
+     * local service socket
+     */
+    int exit_on_close;
 
-        /* the asocket we are connected to
-        */
+    // the asocket we are connected to
+    asocket* peer;
 
-    asocket *peer;
-
-        /* For local asockets, the fde is used to bind
-        ** us to our fd event system.  For remote asockets
-        ** these fields are not used.
-        */
+    /* For local asockets, the fde is used to bind
+     * us to our fd event system.  For remote asockets
+     * these fields are not used.
+     */
     fdevent fde;
     int fd;
 
-        /* queue of apackets waiting to be written
-        */
-    apacket *pkt_first;
-    apacket *pkt_last;
+    // queue of apackets waiting to be written
+    apacket* pkt_first;
+    apacket* pkt_last;
 
-        /* enqueue is called by our peer when it has data
-        ** for us.  It should return 0 if we can accept more
-        ** data or 1 if not.  If we return 1, we must call
-        ** peer->ready() when we once again are ready to
-        ** receive data.
-        */
-    int (*enqueue)(asocket *s, apacket *pkt);
+    /* enqueue is called by our peer when it has data
+     * for us.  It should return 0 if we can accept more
+     * data or 1 if not.  If we return 1, we must call
+     * peer->ready() when we once again are ready to
+     * receive data.
+     */
+    int (*enqueue)(asocket* s, apacket* pkt);
 
-        /* ready is called by the peer when it is ready for
-        ** us to send data via enqueue again
-        */
-    void (*ready)(asocket *s);
+    /* ready is called by the peer when it is ready for
+     * us to send data via enqueue again
+     */
+    void (*ready)(asocket* s);
 
-        /* shutdown is called by the peer before it goes away.
-        ** the socket should not do any further calls on its peer.
-        ** Always followed by a call to close. Optional, i.e. can be NULL.
-        */
-    void (*shutdown)(asocket *s);
+    /* shutdown is called by the peer before it goes away.
+     * the socket should not do any further calls on its peer.
+     * Always followed by a call to close. Optional, i.e. can be NULL.
+     */
+    void (*shutdown)(asocket* s);
 
-        /* close is called by the peer when it has gone away.
-        ** we are not allowed to make any further calls on the
-        ** peer once our close method is called.
-        */
-    void (*close)(asocket *s);
+    /* close is called by the peer when it has gone away.
+     * we are not allowed to make any further calls on the
+     * peer once our close method is called.
+     */
+    void (*close)(asocket* s);
 
-        /* A socket is bound to atransport */
-    atransport *transport;
+    /* A socket is bound to atransport */
+    atransport* transport;
 
     size_t get_max_payload() const;
 };
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e0143c6..c53fbb4 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -96,7 +96,7 @@
 }
 
 void remove_socket(asocket* s) {
-    // socket_list_lock should already be held
+    std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
     if (s->prev && s->next) {
         s->prev->next = s->next;
         s->next->prev = s->prev;
@@ -430,10 +430,11 @@
 }
 
 #if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial) {
+static asocket* create_host_service_socket(const char* name, const char* serial,
+                                           TransportId transport_id) {
     asocket* s;
 
-    s = host_service_to_socket(name, serial);
+    s = host_service_to_socket(name, serial, transport_id);
 
     if (s != NULL) {
         D("LS(%d) bound to '%s'", s->id, name);
@@ -658,6 +659,7 @@
 #if ADB_HOST
     char* service = nullptr;
     char* serial = nullptr;
+    TransportId transport_id = 0;
     TransportType type = kTransportAny;
 #endif
 
@@ -715,6 +717,14 @@
             serial = service;
             service = serial_end + 1;
         }
+    } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
+        service += strlen("host-transport-id:");
+        transport_id = strtoll(service, &service, 10);
+
+        if (*service != ':') {
+            return -1;
+        }
+        service++;
     } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
         type = kTransportUsb;
         service += strlen("host-usb:");
@@ -736,7 +746,7 @@
         ** the OKAY or FAIL message and all we have to do
         ** is clean up.
         */
-        if (handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
+        if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) {
             /* XXX fail message? */
             D("SS(%d): handled host service '%s'", s->id, service);
             goto fail;
@@ -751,7 +761,7 @@
         ** if no such service exists, we'll fail out
         ** and tear down here.
         */
-        s2 = create_host_service_socket(service, serial);
+        s2 = create_host_service_socket(service, serial, transport_id);
         if (s2 == 0) {
             D("SS(%d): couldn't create host service '%s'", s->id, service);
             SendFail(s->peer->fd, "unknown host service");
@@ -783,7 +793,7 @@
 #else /* !ADB_HOST */
     if (s->transport == nullptr) {
         std::string error_msg = "unknown failure";
-        s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+        s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
         if (s->transport == nullptr) {
             SendFail(s->peer->fd, error_msg);
             goto fail;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 49c7847..0abb680 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -582,18 +582,12 @@
 #ifdef __APPLE__
     return pthread_setname_np(name.c_str());
 #else
-    const char *s = name.c_str();
-
-    // pthread_setname_np fails rather than truncating long strings.
-    const int max_task_comm_len = 16; // including the null terminator
-    if (name.length() > (max_task_comm_len - 1)) {
-        char buf[max_task_comm_len];
-        strncpy(buf, name.c_str(), sizeof(buf) - 1);
-        buf[sizeof(buf) - 1] = '\0';
-        s = buf;
-    }
-
-    return pthread_setname_np(pthread_self(), s) ;
+    // Both bionic and glibc's pthread_setname_np fails rather than truncating long strings.
+    // glibc doesn't have strlcpy, so we have to fake it.
+    char buf[16];  // MAX_TASK_COMM_LEN, but that's not exported by the kernel headers.
+    strncpy(buf, name.c_str(), sizeof(buf) - 1);
+    buf[sizeof(buf) - 1] = '\0';
+    return pthread_setname_np(pthread_self(), buf);
 #endif
 }
 
diff --git a/adb/test_adb.py b/adb/test_adb.py
index cb3e0d8..98c8a59 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -60,13 +60,13 @@
                              stderr=subprocess.STDOUT)
         out, _ = p.communicate()
         self.assertEqual(1, p.returncode)
-        self.assertIn('help message', out)
+        self.assertIn('requires an argument', out)
 
         p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
                              stderr=subprocess.STDOUT)
         out, _ = p.communicate()
         self.assertEqual(1, p.returncode)
-        self.assertIn('error', out)
+        self.assertIn('invalid port', out)
 
     # Helper method that reads a pipe until it is closed, then sets the event.
     def _read_pipe_and_set_event(self, pipe, event):
diff --git a/adb/test_device.py b/adb/test_device.py
index 9e1a2ec..ddceda9 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1237,7 +1237,7 @@
                 return m.group(2)
         return None
 
-    def test_killed_when_pushing_a_large_file(self):
+    def disabled_test_killed_when_pushing_a_large_file(self):
         """
            While running adb push with a large file, kill adb server.
            Occasionally the device becomes offline. Because the device is still
@@ -1268,7 +1268,7 @@
         # 4. The device should be online
         self.assertEqual(self._get_device_state(serialno), 'device')
 
-    def test_killed_when_pulling_a_large_file(self):
+    def disabled_test_killed_when_pulling_a_large_file(self):
         """
            While running adb pull with a large file, kill adb server.
            Occasionally the device can't be connected. Because the device is trying to
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 2bbbefd..089a1ec 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -21,6 +21,7 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -36,6 +37,7 @@
 #include <android-base/quick_exit.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
 
 #include "adb.h"
 #include "adb_auth.h"
@@ -46,10 +48,11 @@
 
 static void transport_unref(atransport *t);
 
+// TODO: unordered_map<TransportId, atransport*>
 static auto& transport_list = *new std::list<atransport*>();
 static auto& pending_list = *new std::list<atransport*>();
 
-static std::mutex& transport_lock = *new std::mutex();
+static auto& transport_lock = *new std::recursive_mutex();
 
 const char* const kFeatureShell2 = "shell_v2";
 const char* const kFeatureCmd = "cmd";
@@ -57,6 +60,11 @@
 const char* const kFeatureLibusb = "libusb";
 const char* const kFeaturePushSync = "push_sync";
 
+TransportId NextTransportId() {
+    static std::atomic<TransportId> next(1);
+    return next++;
+}
+
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned command = p->msg.command;
     int len = p->msg.data_length;
@@ -298,9 +306,11 @@
 }
 
 void kick_transport(atransport* t) {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     // As kick_transport() can be called from threads without guarantee that t is valid,
     // check if the transport is in transport_list first.
+    //
+    // TODO(jmgao): WTF? Is this actually true?
     if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
         t->Kick();
     }
@@ -319,7 +329,8 @@
  */
 struct device_tracker {
     asocket socket;
-    int update_needed;
+    bool update_needed;
+    bool long_output;
     device_tracker* next;
 };
 
@@ -330,7 +341,7 @@
     device_tracker** pnode = &device_tracker_list;
     device_tracker* node = *pnode;
 
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     while (node) {
         if (node == tracker) {
             *pnode = node->next;
@@ -376,15 +387,15 @@
 
     // We want to send the device list when the tracker connects
     // for the first time, even if no update occurred.
-    if (tracker->update_needed > 0) {
-        tracker->update_needed = 0;
+    if (tracker->update_needed) {
+        tracker->update_needed = false;
 
-        std::string transports = list_transports(false);
+        std::string transports = list_transports(tracker->long_output);
         device_tracker_send(tracker, transports);
     }
 }
 
-asocket* create_device_tracker(void) {
+asocket* create_device_tracker(bool long_output) {
     device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
@@ -393,7 +404,8 @@
     tracker->socket.enqueue = device_tracker_enqueue;
     tracker->socket.ready = device_tracker_ready;
     tracker->socket.close = device_tracker_close;
-    tracker->update_needed = 1;
+    tracker->update_needed = true;
+    tracker->long_output = long_output;
 
     tracker->next = device_tracker_list;
     device_tracker_list = tracker;
@@ -403,7 +415,7 @@
 
 // Check if all of the USB transports are connected.
 bool iterate_transports(std::function<bool(const atransport*)> fn) {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (const auto& t : transport_list) {
         if (!fn(t)) {
             return false;
@@ -507,7 +519,7 @@
         adb_close(t->fd);
 
         {
-            std::lock_guard<std::mutex> lock(transport_lock);
+            std::lock_guard<std::recursive_mutex> lock(transport_lock);
             transport_list.remove(t);
         }
 
@@ -546,7 +558,7 @@
     }
 
     {
-        std::lock_guard<std::mutex> lock(transport_lock);
+        std::lock_guard<std::recursive_mutex> lock(transport_lock);
         pending_list.remove(t);
         transport_list.push_front(t);
     }
@@ -573,7 +585,7 @@
 
 void kick_all_transports() {
     // To avoid only writing part of a packet to a transport after exit, kick all transports.
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto t : transport_list) {
         t->Kick();
     }
@@ -603,15 +615,15 @@
 static void transport_unref(atransport* t) {
     CHECK(t != nullptr);
 
-    size_t old_refcount = t->ref_count--;
-    CHECK_GT(old_refcount, 0u);
-
-    if (old_refcount == 1u) {
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
+    CHECK_GT(t->ref_count, 0u);
+    t->ref_count--;
+    if (t->ref_count == 0) {
         D("transport: %s unref (kicking and closing)", t->serial);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
+        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
     }
 }
 
@@ -638,11 +650,15 @@
     return !*to_test;
 }
 
-atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
-                                  std::string* error_out, bool accept_any_state) {
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+                                  bool* is_ambiguous, std::string* error_out,
+                                  bool accept_any_state) {
     atransport* result = nullptr;
 
-    if (serial) {
+    if (transport_id != 0) {
+        *error_out =
+            android::base::StringPrintf("no device with transport id '%" PRIu64 "'", transport_id);
+    } else if (serial) {
         *error_out = android::base::StringPrintf("device '%s' not found", serial);
     } else if (type == kTransportLocal) {
         *error_out = "no emulators found";
@@ -652,7 +668,7 @@
         *error_out = "no devices found";
     }
 
-    std::unique_lock<std::mutex> lock(transport_lock);
+    std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& t : transport_list) {
         if (t->GetConnectionState() == kCsNoPerm) {
 #if ADB_HOST
@@ -661,8 +677,12 @@
             continue;
         }
 
-        // Check for matching serial number.
-        if (serial) {
+        if (transport_id) {
+            if (t->id == transport_id) {
+                result = t;
+                break;
+            }
+        } else if (serial) {
             if (t->MatchesTarget(serial)) {
                 if (result) {
                     *error_out = "more than one device";
@@ -728,9 +748,6 @@
 }
 
 int atransport::Write(apacket* p) {
-#if ADB_HOST
-    std::lock_guard<std::mutex> lock(write_msg_lock_);
-#endif
     return write_func_(p, this);
 }
 
@@ -738,11 +755,6 @@
     if (!kicked_) {
         kicked_ = true;
         CHECK(kick_func_ != nullptr);
-#if ADB_HOST
-        // On host, adb server should avoid writing part of a packet, so don't
-        // kick a transport whiling writing a packet.
-        std::lock_guard<std::mutex> lock(write_msg_lock_);
-#endif
         kick_func_(this);
     }
 }
@@ -889,18 +901,23 @@
 
 #if ADB_HOST
 
+// We use newline as our delimiter, make sure to never output it.
+static std::string sanitize(std::string str, bool alphanumeric) {
+    auto pred = alphanumeric ? [](const char c) { return !isalnum(c); }
+                             : [](const char c) { return c == '\n'; };
+    std::replace_if(str.begin(), str.end(), pred, '_');
+    return str;
+}
+
 static void append_transport_info(std::string* result, const char* key, const char* value,
-                                  bool sanitize) {
+                                  bool alphanumeric) {
     if (value == nullptr || *value == '\0') {
         return;
     }
 
     *result += ' ';
     *result += key;
-
-    for (const char* p = value; *p; ++p) {
-        result->push_back((!sanitize || isalnum(*p)) ? *p : '_');
-    }
+    *result += sanitize(value, alphanumeric);
 }
 
 static void append_transport(const atransport* t, std::string* result, bool long_listing) {
@@ -920,6 +937,11 @@
         append_transport_info(result, "product:", t->product, false);
         append_transport_info(result, "model:", t->model, true);
         append_transport_info(result, "device:", t->device, false);
+
+        // Put id at the end, so that anyone parsing the output here can always find it by scanning
+        // backwards from newlines, even with hypothetical devices named 'transport_id:1'.
+        *result += " transport_id:";
+        *result += std::to_string(t->id);
     }
     *result += '\n';
 }
@@ -927,7 +949,7 @@
 std::string list_transports(bool long_listing) {
     std::string result;
 
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (const auto& t : transport_list) {
         append_transport(t, &result, long_listing);
     }
@@ -935,7 +957,7 @@
 }
 
 void close_usb_devices(std::function<bool(const atransport*)> predicate) {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
         if (predicate(t)) {
             t->Kick();
@@ -964,7 +986,7 @@
         return -1;
     }
 
-    std::unique_lock<std::mutex> lock(transport_lock);
+    std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& transport : pending_list) {
         if (transport->serial && strcmp(serial, transport->serial) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
@@ -996,7 +1018,7 @@
 atransport* find_transport(const char* serial) {
     atransport* result = nullptr;
 
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
         if (t->serial && strcmp(serial, t->serial) == 0) {
             result = t;
@@ -1008,7 +1030,7 @@
 }
 
 void kick_all_tcp_devices() {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
         if (t->IsTcpDevice()) {
             // Kicking breaks the read_transport thread of this transport out of any read, then
@@ -1037,7 +1059,7 @@
     }
 
     {
-        std::lock_guard<std::mutex> lock(transport_lock);
+        std::lock_guard<std::recursive_mutex> lock(transport_lock);
         pending_list.push_front(t);
     }
 
@@ -1046,7 +1068,7 @@
 
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb) {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     transport_list.remove_if(
         [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
 }
@@ -1079,11 +1101,4 @@
     keys_.pop_front();
     return result;
 }
-bool atransport::SetSendConnectOnError() {
-    if (has_send_connect_on_error_) {
-        return false;
-    }
-    has_send_connect_on_error_ = true;
-    return true;
-}
 #endif
diff --git a/adb/transport.h b/adb/transport.h
index 4a89ed9..8c101fd 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -54,14 +54,17 @@
 // The server supports `push --sync`.
 extern const char* const kFeaturePushSync;
 
+TransportId NextTransportId();
+
 class atransport {
-public:
+  public:
     // TODO(danalbert): We expose waaaaaaay too much stuff because this was
     // historically just a struct, but making the whole thing a more idiomatic
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
+    atransport(ConnectionState state = kCsOffline)
+        : id(NextTransportId()), connection_state_(state) {
         transport_fde = {};
         protocol_version = A_VERSION;
         max_payload = MAX_PAYLOAD;
@@ -72,12 +75,8 @@
     void (*close)(atransport* t) = nullptr;
 
     void SetWriteFunction(int (*write_func)(apacket*, atransport*)) { write_func_ = write_func; }
-    void SetKickFunction(void (*kick_func)(atransport*)) {
-        kick_func_ = kick_func;
-    }
-    bool IsKicked() {
-        return kicked_;
-    }
+    void SetKickFunction(void (*kick_func)(atransport*)) { kick_func_ = kick_func; }
+    bool IsKicked() { return kicked_; }
     int Write(apacket* p);
     void Kick();
 
@@ -85,10 +84,11 @@
     ConnectionState GetConnectionState() const;
     void SetConnectionState(ConnectionState state);
 
+    const TransportId id;
     int fd = -1;
     int transport_socket = -1;
     fdevent transport_fde;
-    std::atomic<size_t> ref_count;
+    size_t ref_count = 0;
     uint32_t sync_token = 0;
     bool online = false;
     TransportType type = kTransportAny;
@@ -122,7 +122,6 @@
 
 #if ADB_HOST
     std::shared_ptr<RSA> NextKey();
-    bool SetSendConnectOnError();
 #endif
 
     char token[TOKEN_SIZE] = {};
@@ -181,8 +180,6 @@
     std::atomic<ConnectionState> connection_state_;
 #if ADB_HOST
     std::deque<std::shared_ptr<RSA>> keys_;
-    std::mutex write_msg_lock_;
-    bool has_send_connect_on_error_ = false;
 #endif
 
     DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -191,12 +188,14 @@
 /*
  * Obtain a transport from the available transports.
  * If serial is non-null then only the device with that serial will be chosen.
+ * If transport_id is non-zero then only the device with that transport ID will be chosen.
  * If multiple devices/emulators would match, *is_ambiguous (if non-null)
  * is set to true and nullptr returned.
  * If no suitable transport is found, error is set and nullptr returned.
  */
-atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
-                                  std::string* error_out, bool accept_any_state = false);
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+                                  bool* is_ambiguous, std::string* error_out,
+                                  bool accept_any_state = false);
 void kick_transport(atransport* t);
 void update_transports(void);
 
@@ -231,6 +230,6 @@
 
 void send_packet(apacket* p, atransport* t);
 
-asocket* create_device_tracker(void);
+asocket* create_device_tracker(bool long_output);
 
 #endif   /* __TRANSPORT_H */
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 809ed89..9cd378c 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -388,6 +388,25 @@
     D("transport: qemu_socket_thread() exiting");
     return;
 }
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+static bool use_qemu_goldfish() {
+    // Legacy way to detect if adbd should use the goldfish pipe is to check for
+    // ro.kernel.qemu, keep that behaviour for backward compatibility.
+    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+        return true;
+    }
+    // If service.adb.transport is present and is set to "goldfish", use the
+    // QEMUD pipe.
+    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+        return true;
+    }
+    return false;
+}
+
 #endif  // !ADB_HOST
 
 void local_init(int port)
@@ -401,13 +420,7 @@
 #else
     // For the adbd daemon in the system image we need to distinguish
     // between the device, and the emulator.
-    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
-        // Running inside the emulator: use QEMUD pipe as the transport.
-        func = qemu_socket_thread;
-    } else {
-        // Running inside the device: use TCP socket as the transport.
-        func = server_socket_thread;
-    }
+    func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
     debug_name = "server";
 #endif // !ADB_HOST
 
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 7e8ae67..fdecccf 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -33,8 +33,8 @@
     D("UsbReadMessage");
 
     size_t usb_packet_size = usb_get_max_packet_size(h);
-    CHECK(usb_packet_size >= sizeof(*msg));
-    CHECK(usb_packet_size < 4096);
+    CHECK_GE(usb_packet_size, sizeof(*msg));
+    CHECK_LT(usb_packet_size, 4096ULL);
 
     char buffer[4096];
     int n = usb_read(h, buffer, usb_packet_size);
@@ -52,7 +52,7 @@
     D("UsbReadPayload(%d)", p->msg.data_length);
 
     size_t usb_packet_size = usb_get_max_packet_size(h);
-    CHECK(sizeof(p->data) % usb_packet_size == 0);
+    CHECK_EQ(0ULL, sizeof(p->data) % usb_packet_size);
 
     // Round the data length up to the nearest packet size boundary.
     // The device won't send a zero packet for packet size aligned payloads,
@@ -62,7 +62,7 @@
     if (rem_size) {
         len += usb_packet_size - rem_size;
     }
-    CHECK(len <= sizeof(p->data));
+    CHECK_LE(len, sizeof(p->data));
     return usb_read(h, &p->data, len);
 }
 
@@ -103,13 +103,6 @@
 
 err_msg:
     p->msg.command = 0;
-    if (t->GetConnectionState() == kCsOffline) {
-        // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
-        // an error message. In this case, resend one A_CNXN message to connect the device.
-        if (t->SetSendConnectOnError()) {
-            SendConnectOnHost(t);
-        }
-    }
     return 0;
 }
 
@@ -162,8 +155,7 @@
     return 0;
 }
 
-static void remote_close(atransport *t)
-{
+static void remote_close(atransport* t) {
     usb_close(t->usb);
     t->usb = 0;
 }
diff --git a/base/Android.bp b/base/Android.bp
index b636dc3..f4a8411 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -39,8 +39,11 @@
 cc_library {
     name: "libbase",
     vendor_available: true,
-    clang: true,
     host_supported: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     srcs: [
         "file.cpp",
         "logging.cpp",
@@ -61,16 +64,20 @@
     target: {
         android: {
             srcs: [
-                "errors_unix.cpp",
                 "properties.cpp",
-                "chrono_utils.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
 
         },
+        linux: {
+            srcs: [
+                "chrono_utils.cpp",
+                "errors_unix.cpp",
+            ],
+            cppflags: ["-Wexit-time-destructors"],
+        },
         darwin: {
             srcs: [
                 "chrono_utils.cpp",
@@ -79,21 +86,8 @@
             cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
-            srcs: [
-                "chrono_utils.cpp",
-                "errors_unix.cpp",
-            ],
-            cppflags: ["-Wexit-time-destructors"],
             enabled: true,
         },
-        linux: {
-            srcs: [
-                "chrono_utils.cpp",
-                "errors_unix.cpp",
-            ],
-            cppflags: ["-Wexit-time-destructors"],
-            host_ldlibs: ["-lrt"],
-        },
         windows: {
             srcs: [
                 "errors_windows.cpp",
@@ -109,7 +103,6 @@
 cc_test {
     name: "libbase_test",
     host_supported: true,
-    clang: true,
     srcs: [
         "endian_test.cpp",
         "errors_test.cpp",
@@ -126,17 +119,13 @@
     ],
     target: {
         android: {
-            srcs: [
-                "chrono_utils_test.cpp",
-                "properties_test.cpp"
-            ],
+            srcs: ["properties_test.cpp"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
         linux: {
             srcs: ["chrono_utils_test.cpp"],
-            host_ldlibs: ["-lrt"],
         },
         windows: {
             srcs: ["utf8_test.cpp"],
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index 5eedf3b..dbe5483 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -22,7 +22,7 @@
 namespace base {
 
 boot_clock::time_point boot_clock::now() {
-#ifdef __ANDROID__
+#ifdef __linux__
   timespec ts;
   clock_gettime(CLOCK_BOOTTIME, &ts);
   return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
@@ -30,7 +30,12 @@
 #else
   // Darwin does not support clock_gettime.
   return boot_clock::time_point();
-#endif  // __ANDROID__
+#endif  // __linux__
+}
+
+std::ostream& operator<<(std::ostream& os, const Timer& t) {
+  os << t.duration().count() << "ms";
+  return os;
 }
 
 }  // namespace base
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
index 057132d..da442f4 100644
--- a/base/chrono_utils_test.cpp
+++ b/base/chrono_utils_test.cpp
@@ -19,6 +19,9 @@
 #include <time.h>
 
 #include <chrono>
+#include <sstream>
+#include <string>
+#include <thread>
 
 #include <gtest/gtest.h>
 
@@ -42,5 +45,36 @@
   EXPECT_EQ(now, boot_seconds);
 }
 
+template <typename T>
+void ExpectAboutEqual(T expected, T actual) {
+  auto expected_upper_bound = expected * 1.05f;
+  auto expected_lower_bound = expected * .95;
+  EXPECT_GT(expected_upper_bound, actual);
+  EXPECT_LT(expected_lower_bound, actual);
+}
+
+TEST(ChronoUtilsTest, TimerDurationIsSane) {
+  auto start = boot_clock::now();
+  Timer t;
+  std::this_thread::sleep_for(50ms);
+  auto stop = boot_clock::now();
+  auto stop_timer = t.duration();
+
+  auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
+  ExpectAboutEqual(expected, stop_timer);
+}
+
+TEST(ChronoUtilsTest, TimerOstream) {
+  Timer t;
+  std::this_thread::sleep_for(50ms);
+  auto stop_timer = t.duration().count();
+  std::stringstream os;
+  os << t;
+  decltype(stop_timer) stop_timer_from_stream;
+  os >> stop_timer_from_stream;
+  EXPECT_NE(0, stop_timer);
+  ExpectAboutEqual(stop_timer, stop_timer_from_stream);
+}
+
 }  // namespace base
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/base/file.cpp b/base/file.cpp
index a2f2887..2f697a1 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -153,6 +153,37 @@
   return true;
 }
 
+#if defined(_WIN32)
+// Windows implementation of pread. Note that this DOES move the file descriptors read position,
+// but it does so atomically.
+static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
+  DWORD bytes_read;
+  OVERLAPPED overlapped;
+  memset(&overlapped, 0, sizeof(OVERLAPPED));
+  overlapped.Offset = static_cast<DWORD>(offset);
+  overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+  if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count),
+                &bytes_read, &overlapped)) {
+    // In case someone tries to read errno (since this is masquerading as a POSIX call)
+    errno = EIO;
+    return -1;
+  }
+  return static_cast<ssize_t>(bytes_read);
+}
+#endif
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+  uint8_t* p = reinterpret_cast<uint8_t*>(data);
+  while (byte_count > 0) {
+    ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset));
+    if (n <= 0) return false;
+    p += n;
+    byte_count -= n;
+    offset += n;
+  }
+  return true;
+}
+
 bool WriteFully(int fd, const void* data, size_t byte_count) {
   const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
   size_t remaining = byte_count;
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index 0086425..7679d4c 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -18,6 +18,9 @@
 #define ANDROID_BASE_CHRONO_UTILS_H
 
 #include <chrono>
+#include <sstream>
+
+using namespace std::chrono_literals;
 
 namespace android {
 namespace base {
@@ -31,6 +34,20 @@
   static time_point now();
 };
 
+class Timer {
+ public:
+  Timer() : start_(boot_clock::now()) {}
+
+  std::chrono::milliseconds duration() const {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
+  }
+
+ private:
+  boot_clock::time_point start_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Timer& t);
+
 }  // namespace base
 }  // namespace android
 
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 651f529..667d6fb 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -18,12 +18,18 @@
 #define ANDROID_BASE_FILE_H
 
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <string>
 
 #if !defined(_WIN32) && !defined(O_BINARY)
 #define O_BINARY 0
 #endif
 
+#if defined(__APPLE__)
+/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
+
 namespace android {
 namespace base {
 
@@ -42,6 +48,17 @@
 #endif
 
 bool ReadFully(int fd, void* data, size_t byte_count);
+
+// Reads `byte_count` bytes from the file descriptor at the specified offset.
+// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
+//
+// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
+// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
+// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
+// same function, but concurrently seeking or reading incrementally can lead to unexpected
+// behavior.
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset);
+
 bool WriteFully(int fd, const void* data, size_t byte_count);
 
 bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 548b286..f93c696 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -438,4 +438,36 @@
 }  // namespace base
 }  // namespace android
 
+namespace std {
+
+// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
+//
+// Note: for this to work, we need to have this in a namespace.
+// Note: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools)
+// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common
+//       attribute support.
+// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
+//       diagnose_if.
+// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
+// Note: a not-recommended alternative is to let Clang ignore the warning by adding
+//       -Wno-user-defined-warnings to CPPFLAGS.
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+#define OSTREAM_STRING_POINTER_USAGE_WARNING \
+    __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
+#else
+#define OSTREAM_STRING_POINTER_USAGE_WARNING /* empty */
+#endif
+inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
+    OSTREAM_STRING_POINTER_USAGE_WARNING {
+  return stream << static_cast<const void*>(string_pointer);
+}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+#undef OSTREAM_STRING_POINTER_USAGE_WARNING
+
+}  // namespace std
+
 #endif  // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 88bbe8a..25f2ff4 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -179,4 +179,19 @@
   } while (0)
 #endif
 
+// Current ABI string
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#endif
+
 #endif  // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index c0bf0c1..07a5edd 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -26,6 +26,10 @@
   TemporaryFile();
   ~TemporaryFile();
 
+  // Release the ownership of fd, caller is reponsible for closing the
+  // fd or stream properly.
+  int release();
+
   int fd;
   char path[1024];
 
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 6cfcfcd..5d89271 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -17,6 +17,13 @@
 #ifndef ANDROID_BASE_UNIQUE_FD_H
 #define ANDROID_BASE_UNIQUE_FD_H
 
+#include <fcntl.h>
+
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#endif
+
+#include <sys/types.h>
 #include <unistd.h>
 
 // DO NOT INCLUDE OTHER LIBBASE HEADERS!
@@ -88,6 +95,49 @@
 
 using unique_fd = unique_fd_impl<DefaultCloser>;
 
+#if !defined(_WIN32)
+
+// Inline functions, so that they can be used header-only.
+inline bool Pipe(unique_fd* read, unique_fd* write) {
+  int pipefd[2];
+
+#if defined(__linux__)
+  if (pipe2(pipefd, O_CLOEXEC) != 0) {
+    return false;
+  }
+#else  // defined(__APPLE__)
+  if (pipe(pipefd) != 0) {
+    return false;
+  }
+
+  if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+    close(pipefd[0]);
+    close(pipefd[1]);
+    return false;
+  }
+#endif
+
+  read->reset(pipefd[0]);
+  write->reset(pipefd[1]);
+  return true;
+}
+
+inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) {
+  int sockfd[2];
+  if (socketpair(domain, type, protocol, sockfd) != 0) {
+    return false;
+  }
+  left->reset(sockfd[0]);
+  right->reset(sockfd[1]);
+  return true;
+}
+
+inline bool Socketpair(int type, unique_fd* left, unique_fd* right) {
+  return Socketpair(AF_UNIX, type, 0, left, right);
+}
+
+#endif  // !defined(_WIN32)
+
 }  // namespace base
 }  // namespace android
 
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 2d5a6f6..c9cc1ab 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -22,6 +22,8 @@
 #else
 // Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
 #include <fcntl.h>      // open
+#include <stdio.h>      // fopen
+#include <sys/stat.h>   // mkdir
 #include <unistd.h>     // unlink
 #endif
 
@@ -53,6 +55,19 @@
 // Convert a UTF-8 std::string (including any embedded NULL characters) to
 // UTF-16. Returns whether the conversion was done successfully.
 bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+
+// Convert a file system path, represented as a NULL-terminated string of
+// UTF-8 characters, to a UTF-16 string representing the same file system
+// path using the Windows extended-lengh path representation.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
+//   ```The Windows API has many functions that also have Unicode versions to
+//   permit an extended-length path for a maximum total path length of 32,767
+//   characters. To specify an extended-length path, use the "\\?\" prefix.
+//   For example, "\\?\D:\very long path".```
+//
+// Returns whether the conversion was done successfully.
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
 #endif
 
 // The functions in the utf8 namespace take UTF-8 strings. For Windows, these
@@ -73,9 +88,13 @@
 namespace utf8 {
 
 #ifdef _WIN32
+FILE* fopen(const char* name, const char* mode);
+int mkdir(const char* name, mode_t mode);
 int open(const char* name, int flags, ...);
 int unlink(const char* name);
 #else
+using ::fopen;
+using ::mkdir;
 using ::open;
 using ::unlink;
 #endif
diff --git a/base/properties.cpp b/base/properties.cpp
index 816bca0..cde4d69 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -36,13 +36,18 @@
   const prop_info* pi = __system_property_find(key.c_str());
   if (pi == nullptr) return default_value;
 
-  char buf[PROP_VALUE_MAX];
-  if (__system_property_read(pi, nullptr, buf) > 0) return buf;
+  std::string property_value;
+  __system_property_read_callback(pi,
+                                  [](void* cookie, const char*, const char* value, unsigned) {
+                                    auto property_value = reinterpret_cast<std::string*>(cookie);
+                                    *property_value = value;
+                                  },
+                                  &property_value);
 
   // If the property exists but is empty, also return the default value.
   // Since we can't remove system properties, "empty" is traditionally
   // the same as "missing" (this was true for cutils' property_get).
-  return default_value;
+  return property_value.empty() ? default_value : property_value;
 }
 
 bool GetBoolProperty(const std::string& key, bool default_value) {
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 636477d..1cfa9e6 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -85,10 +85,18 @@
 }
 
 TemporaryFile::~TemporaryFile() {
-  close(fd);
+  if (fd != -1) {
+    close(fd);
+  }
   unlink(path);
 }
 
+int TemporaryFile::release() {
+  int result = fd;
+  fd = -1;
+  return result;
+}
+
 void TemporaryFile::init(const std::string& tmp_dir) {
   snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
            OS_PATH_SEPARATOR);
diff --git a/base/utf8.cpp b/base/utf8.cpp
index 3cca700..5984fb0 100644
--- a/base/utf8.cpp
+++ b/base/utf8.cpp
@@ -19,7 +19,9 @@
 #include "android-base/utf8.h"
 
 #include <fcntl.h>
+#include <stdio.h>
 
+#include <algorithm>
 #include <string>
 
 #include "android-base/logging.h"
@@ -153,12 +155,58 @@
   return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
 }
 
+static bool isDriveLetter(wchar_t c) {
+  return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
+}
+
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
+  if (!UTF8ToWide(utf8, utf16)) {
+    return false;
+  }
+  // Note: Although most Win32 File I/O API are limited to MAX_PATH (260
+  //       characters), the CreateDirectory API is limited to 248 characters.
+  if (utf16->length() >= 248) {
+    // If path is of the form "x:\" or "x:/"
+    if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
+        ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
+      // Append long path prefix, and make sure there are no unix-style
+      // separators to ensure a fully compliant Win32 long path string.
+      utf16->insert(0, LR"(\\?\)");
+      std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
+    }
+  }
+  return true;
+}
+
 // Versions of standard library APIs that support UTF-8 strings.
 namespace utf8 {
 
+FILE* fopen(const char* name, const char* mode) {
+  std::wstring name_utf16;
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+    return nullptr;
+  }
+
+  std::wstring mode_utf16;
+  if (!UTF8ToWide(mode, &mode_utf16)) {
+    return nullptr;
+  }
+
+  return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
+}
+
+int mkdir(const char* name, mode_t mode) {
+  std::wstring name_utf16;
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+    return -1;
+  }
+
+  return _wmkdir(name_utf16.c_str());
+}
+
 int open(const char* name, int flags, ...) {
   std::wstring name_utf16;
-  if (!UTF8ToWide(name, &name_utf16)) {
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
     return -1;
   }
 
@@ -175,7 +223,7 @@
 
 int unlink(const char* name) {
   std::wstring name_utf16;
-  if (!UTF8ToWide(name, &name_utf16)) {
+  if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
     return -1;
   }
 
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
index ae8fc8c..fcb25c3 100644
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -18,7 +18,12 @@
 
 #include <gtest/gtest.h>
 
+#include <fcntl.h>
+#include <stdlib.h>
+
 #include "android-base/macros.h"
+#include "android-base/test_utils.h"
+#include "android-base/unique_fd.h"
 
 namespace android {
 namespace base {
@@ -408,5 +413,76 @@
   EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
 }
 
+TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
+  std::string utf8 = "c:\\mypath\\myfile.txt";
+
+  std::wstring wide;
+  EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+  EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
+  std::string utf8 = "c:\\mypath";
+  while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+    utf8 += "\\mypathsegment";
+  }
+
+  std::wstring wide;
+  EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+  EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+  EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
+  std::string utf8 = "c:/mypath";
+  while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+    utf8 += "/mypathsegment";
+  }
+
+  std::wstring wide;
+  EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+  EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+  EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+namespace utf8 {
+
+TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
+  TemporaryDir td;
+
+  // Create long directory path
+  std::string utf8 = td.path;
+  while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+    utf8 += "\\mypathsegment";
+    EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
+  }
+
+  // Create file
+  utf8 += "\\test-file.bin";
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+  int mode = 0666;
+  android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
+  EXPECT_NE(-1, fd.get());
+
+  // Close file
+  fd.reset();
+  EXPECT_EQ(-1, fd.get());
+
+  // Open file with fopen
+  FILE* file = fopen(utf8.c_str(), "rb");
+  EXPECT_NE(nullptr, file);
+
+  if (file) {
+    fclose(file);
+  }
+
+  // Delete file
+  EXPECT_EQ(0, unlink(utf8.c_str()));
+}
+
+}  // namespace utf8
 }  // namespace base
 }  // namespace android
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index bc90a6e..2c87018 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -32,9 +32,6 @@
         "liblog",
         "libmetricslogger",
     ],
-    whole_static_libs: ["libgtest_prod"],
-    // Clang is required because of C++14
-    clang: true,
 }
 
 // bootstat static library
@@ -66,7 +63,13 @@
     name: "bootstat",
     defaults: ["bootstat_defaults"],
     static_libs: ["libbootstat"],
+    shared_libs: ["liblogcat"],
     init_rc: ["bootstat.rc"],
+    product_variables: {
+        debuggable: {
+            init_rc: ["bootstat-debug.rc"],
+        },
+    },
     srcs: ["bootstat.cpp"],
 }
 
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 99d9405..e2a4b04 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -58,16 +58,15 @@
 }
 
 void BootEventRecordStore::AddBootEvent(const std::string& event) {
-    auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
-        android::base::boot_clock::now().time_since_epoch());
-    AddBootEventWithValue(event, uptime.count());
+  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+      android::base::boot_clock::now().time_since_epoch());
+  AddBootEventWithValue(event, uptime.count());
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
 // attribute to store the value associated with a boot event in order to
 // optimize on-disk size requirements and small-file thrashing.
-void BootEventRecordStore::AddBootEventWithValue(
-    const std::string& event, int32_t value) {
+void BootEventRecordStore::AddBootEventWithValue(const std::string& event, int32_t value) {
   std::string record_path = GetBootEventPath(event);
   int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR);
   if (record_fd == -1) {
@@ -96,8 +95,7 @@
   close(record_fd);
 }
 
-bool BootEventRecordStore::GetBootEvent(
-    const std::string& event, BootEventRecord* record) const {
+bool BootEventRecordStore::GetBootEvent(const std::string& event, BootEventRecord* record) const {
   CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);
   CHECK(!event.empty());
 
@@ -112,8 +110,7 @@
   return true;
 }
 
-std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
-    GetAllBootEvents() const {
+std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::GetAllBootEvents() const {
   std::vector<BootEventRecord> events;
 
   std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir);
@@ -147,8 +144,7 @@
   store_path_ = path;
 }
 
-std::string BootEventRecordStore::GetBootEventPath(
-    const std::string& event) const {
+std::string BootEventRecordStore::GetBootEventPath(const std::string& event) const {
   DCHECK_EQ('/', store_path_.back());
   return store_path_ + event;
 }
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index a2b8318..f872c85 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -17,12 +17,12 @@
 #ifndef BOOT_EVENT_RECORD_STORE_H_
 #define BOOT_EVENT_RECORD_STORE_H_
 
+#include <android-base/macros.h>
+#include <gtest/gtest_prod.h>
 #include <cstdint>
 #include <string>
 #include <utility>
 #include <vector>
-#include <android-base/macros.h>
-#include <gtest/gtest_prod.h>
 
 // BootEventRecordStore manages the persistence of boot events to the record
 // store and the retrieval of all boot event records from the store.
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index d98169b..4b7ab36 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -94,20 +94,16 @@
 
 // Returns the time in seconds since boot.
 time_t GetUptimeSeconds() {
-    return std::chrono::duration_cast<std::chrono::seconds>(
-               android::base::boot_clock::now().time_since_epoch())
-        .count();
+  return std::chrono::duration_cast<std::chrono::seconds>(
+             android::base::boot_clock::now().time_since_epoch())
+      .count();
 }
 
 class BootEventRecordStoreTest : public ::testing::Test {
  public:
-  BootEventRecordStoreTest() {
-    store_path_ = std::string(store_dir_.path) + "/";
-  }
+  BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + "/"; }
 
-  const std::string& GetStorePathForTesting() const {
-    return store_path_;
-  }
+  const std::string& GetStorePathForTesting() const { return store_path_; }
 
  private:
   void TearDown() {
@@ -159,9 +155,7 @@
   store.AddBootEvent("triassic");
 
   const std::string EXPECTED_NAMES[] = {
-    "cretaceous",
-    "jurassic",
-    "triassic",
+      "cretaceous", "jurassic", "triassic",
   };
 
   auto events = store.GetAllBootEvents();
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
new file mode 100755
index 0000000..209e81b
--- /dev/null
+++ b/bootstat/boot_reason_test.sh
@@ -0,0 +1,1069 @@
+#! /bin/bash
+#
+# Bootstat boot reason tests
+#
+# throughout testing:
+# - manual tests can only run on eng/userdebug builds
+# - watch adb logcat -b all -d -s bootstat
+# - watch adb logcat -b all -d | audit2allow
+# - wait until screen is up, boot has completed, can mean wait for
+#   sys.boot_completed=1 and sys.logbootcomplete=1 to be true
+#
+# All test frames, and nothing else, must be function names prefixed and
+# specifiged with the pattern 'test_<test>() {' as this is also how the
+# script discovers the full list of tests by inspecting its own code.
+#
+
+# Helper variables
+
+SPACE=" "
+ESCAPE=""
+TAB="	"
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+NORMAL="${ESCAPE}[0m"
+# Best guess to an average device's reboot time, refined as tests return
+DURATION_DEFAULT=45
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+  fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+  adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: hasPstore
+
+Returns: true if device (likely) has pstore data" ]
+hasPstore() {
+  if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+    false
+  fi
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+  if inAdb && [ 1 -ne `adb shell getprop ro.debuggable` ]; then
+    false
+  fi
+}
+
+[ "USAGE: checkDebugBuild [--noerror]
+
+Returns: true if device is a userdebug or eng release" ]
+checkDebugBuild() {
+  if isDebuggable; then
+    echo "INFO: '${TEST}' test requires userdebug build"
+  elif [ -n "${1}" ]; then
+    echo "WARNING: '${TEST}' test requires userdebug build"
+    false
+  else
+    echo "ERROR: '${TEST}' test requires userdebug build, skipping FAILURE"
+    duration_prefix="~"
+    duration_estimate=1
+    false
+  fi >&2
+}
+
+[ "USAGE: setBootloaderBootReason [value]
+
+Returns: true if device supports and set boot reason injection" ]
+setBootloaderBootReason() {
+  inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
+  if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then
+    echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
+    return 1
+  fi
+  checkDebugBuild || return 1
+  if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" |
+     grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
+    echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
+    echo "       does not set androidboot.bootreason kernel parameter." >&2
+    return 1
+  fi
+  adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
+  test_reason="`adb shell getprop persist.test.boot.reason 2>/dev/null`"
+  if [ X"${test_reason}" != X"${1}" ]; then
+    echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
+    return 1
+  fi
+}
+
+[ "USAGE: enterPstore
+
+Prints a warning string requiring functional pstore
+
+Returns: pstore_ok variable set to true or false" ]
+enterPstore() {
+  if hasPstore; then
+    echo "INFO: '${TEST}' test requires functional and reliable pstore"
+    pstore_ok=true
+  else
+    echo "WARNING: '${TEST}' test requires functional pstore"
+    pstore_ok=false
+  fi >&2
+  ${pstore_ok}
+}
+
+[ "USAGE: exitPstore
+
+Prints an error string requiring functional pstore
+
+Returns: clears error if pstore dysfunctional" ]
+exitPstore() {
+  save_ret=${?}
+  if [ ${save_ret} != 0 ]; then
+    if hasPstore; then
+      return ${save_ret}
+    fi
+    if [ true = ${pstore_ok} ]; then
+      echo "WARNING: '${TEST}' test requires functional pstore"
+      return ${save_ret}
+    fi
+    echo "ERROR: '${TEST}' test requires functional pstore, skipping FAILURE"
+    duration_prefix="~"
+    duration_estimate=1
+  fi >&2
+}
+
+[ "USAGE: format_duration <seconds>
+
+human readable output whole seconds, whole minutes or mm:ss" ]
+format_duration() {
+  if [ -z "${1}" ]; then
+    echo unknown
+    return
+  fi
+  seconds=`expr ${1} % 60`
+  minutes=`expr ${1} / 60`
+  if [ 0 -eq ${minutes} ]; then
+    if [ 1 -eq ${1} ]; then
+      echo 1 second
+      return
+    fi
+    echo ${1} seconds
+    return
+  elif [ 60 -eq ${1} ]; then
+    echo 1 minute
+    return
+  elif [ 0 -eq ${seconds} ]; then
+    echo ${minutes} minutes
+    return
+  fi
+  echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+}
+
+wait_for_screen_timeout=900
+[ "USAGE: wait_for_screen [-n] [TIMEOUT]
+
+-n - echo newline at exit
+TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
+wait_for_screen() {
+  exit_function=true
+  if [ X"-n" = X"${1}" ]; then
+    exit_function=echo
+    shift
+  fi
+  timeout=${wait_for_screen_timeout}
+  if [ ${#} -gt 0 ]; then
+    timeout=${1}
+    shift
+  fi
+  counter=0
+  while true; do
+    if inFastboot; then
+      fastboot reboot
+    elif inAdb; then
+      if [ 0 != ${counter} ]; then
+        adb wait-for-device </dev/null >/dev/null 2>/dev/null
+      fi
+      if [ -n "`adb shell getprop sys.boot.reason </dev/null 2>/dev/null`" ]
+      then
+        vals=`adb shell getprop </dev/null 2>/dev/null |
+              sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+        if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
+        then
+          break
+        fi
+        if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
+        then
+          break
+        fi
+      fi
+    fi
+    counter=`expr ${counter} + 1`
+    if [ ${counter} -gt ${timeout} ]; then
+      ${exit_function}
+      echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+      return 1
+    fi
+    sleep 1
+  done
+  ${exit_function}
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+  lval="${1}"
+  rval="${2}"
+  shift 2
+  if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+    echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
+    if [ -n "${*}" ] ; then
+      echo "       ${*}" >&2
+    fi
+    return 1
+  fi
+  if [ -n "${*}" ] ; then
+    if [ X"${lval}" != X"${rval}" ]; then
+      echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
+    else
+      echo "INFO: ok \"${lval}\" ${*}" >&2
+    fi
+  fi
+  return 0
+}
+
+[ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
+
+Returns true if current return (regex) value is true and the result matches" ]
+EXPECT_PROPERTY() {
+  save_ret=${?}
+  property="${1}"
+  value="${2}"
+  shift 2
+  val=`adb shell getprop ${property} 2>&1`
+  EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
+    [ -n "${1}" ] ||
+    save_ret=${?}
+  return ${save_ret}
+}
+
+[ "USAGE: report_bootstat_logs <expected> ...
+
+if not prefixed with a minus (-), <expected> will become a series of expected
+matches:
+
+    bootstat: Canonical boot reason: <expected_property_value>
+
+If prefixed with a minus, <expected> will look for an exact match after
+removing the minux prefix.  All expected content is _dropped_ from the output
+and in essence forms a known blacklist, unexpected content will show.
+
+Report any logs, minus a known blacklist, preserve the current exit status" ]
+report_bootstat_logs() {
+  save_ret=${?}
+  match=
+  for i in "${@}"; do
+    if [ X"${i}" != X"${i#-}" ] ; then
+      match="${match}
+${i#-}"
+    else
+      match="${match}
+bootstat: Canonical boot reason: ${i}"
+    fi
+  done
+  adb logcat -b all -d |
+  grep bootstat[^e] |
+  grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
+bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
+bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
+bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
+bootstat: Service started: /system/bin/bootstat -l
+bootstat: Battery level at shutdown 100%
+bootstat: Battery level at startup 100%
+init    : Parsing file /system/etc/init/bootstat.rc...
+init    : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc:
+init    : Command 'setprop ro.boot.bootreason \${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc:
+init    : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
+init    : processing action (boot) from (/system/etc/init/bootstat.rc
+init    : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init    : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
+ (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
+ (/system/bin/bootstat -r post_decrypt_time_elapsed)'
+init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+ (/system/bin/bootstat --record_boot_complete)'...
+ (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
+ (/system/bin/bootstat --record_boot_reason)'...
+ (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
+ (/system/bin/bootstat --record_time_since_factory_reset)'...
+ (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat -l)'...
+ (/system/bin/bootstat -l)' (pid " |
+  grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
+  return ${save_ret}
+}
+
+[ "USAGE: start_test [message]
+
+Record start of test, preserve exit status" ]
+start_test() {
+  save_ret=${?}
+  duration_prefix="~"
+  duration_estimate=1
+  START=`date +%s`
+  echo "${GREEN}[ RUN      ]${NORMAL} ${TEST} ${*}"
+  return ${save_ret}
+}
+
+duration_sum_diff=0
+duration_num=0
+[ "USAGE: duration_test [[prefix]seconds]
+
+Report the adjusted and expected test duration" ]
+duration_test() {
+  duration_prefix=${1%%[0123456789]*}
+  if [ -z "${duration_prefix}" ]; then
+    duration_prefix="~"
+  fi
+  duration_estimate="${1#${duration_prefix}}"
+  if [ -z "${duration_estimate}" ]; then
+    duration_estimate="${DURATION_DEFAULT}"
+  fi
+  duration_new_estimate="${duration_estimate}"
+  if [ 0 -ne ${duration_num} ]; then
+    duration_new_estimate=`expr ${duration_new_estimate} + \
+      \( ${duration_num} / 2 + ${duration_sum_diff} \) / ${duration_num}`
+    # guard against catastrophe
+    if [ -z "${duration_new_estimate}" ]; then
+      duration_new_estimate=${duration_estimate}
+    fi
+  fi
+  # negative values are so undignified
+  if [ 0 -ge ${duration_new_estimate} ]; then
+    duration_new_estimate=1
+  fi
+  echo "INFO: expected duration of '${TEST}' test" \
+       "${duration_prefix}`format_duration ${duration_new_estimate}`" >&2
+}
+
+[ "USAGE: end_test [message]
+
+Document duration and success of test, preserve exit status" ]
+end_test() {
+  save_ret=${?}
+  END=`date +%s`
+  duration=`expr ${END} - ${START} 2>/dev/null`
+  [ 0 -ge ${duration} ] ||
+    echo "INFO: '${TEST}' test duration `format_duration ${duration}`" >&2
+  if [ ${save_ret} = 0 ]; then
+    if [ 0 -lt ${duration} -a 0 -lt ${duration_estimate} -a \( \
+           X"~" = X"${duration_prefix}" -o \
+           ${duration_estimate} -gt ${duration} \) ]; then
+      duration_sum_diff=`expr ${duration_sum_diff} + \
+                              ${duration} - ${duration_estimate}`
+      duration_num=`expr ${duration_num} + 1`
+    fi
+    echo "${GREEN}[       OK ]${NORMAL} ${TEST} ${*}"
+  else
+    echo "${RED}[  FAILED  ]${NORMAL} ${TEST} ${*}"
+  fi
+  return ${save_ret}
+}
+
+[ "USAGE: wrap_test <test> [message]
+
+All tests below are wrapped with this helper" ]
+wrap_test() {
+  if [ -z "${1}" -o X"nothing" = X"${1}" ]; then
+    return
+  fi
+  TEST=${1}
+  shift
+  start_test ${1}
+  eval test_${TEST}
+  end_test ${2}
+}
+
+[ "USAGE: validate_reason <value>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_reason() {
+  var=`echo -n ${*} |
+       tr '[A-Z]' '[a-z]' |
+       tr ' \f\t\r\n' '_____'`
+  case ${var} in
+    watchdog | watchdog,?* ) ;;
+    kernel_panic | kernel_panic,?*) ;;
+    recovery | recovery,?*) ;;
+    bootloader | bootloader,?*) ;;
+    cold | cold,?*) ;;
+    hard | hard,?*) ;;
+    warm | warm,?*) ;;
+    shutdown | shutdown,?*) ;;
+    reboot,reboot | reboot,reboot,* )     var=${var#reboot,} ; var=${var%,} ;;
+    reboot,cold | reboot,cold,* )         var=${var#reboot,} ; var=${var%,} ;;
+    reboot,hard | reboot,hard,* )         var=${var#reboot,} ; var=${var%,} ;;
+    reboot,warm | reboot,warm,* )         var=${var#reboot,} ; var=${var%,} ;;
+    reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;;
+    reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
+    reboot | reboot,?*) ;;
+    # Aliases and Heuristics
+    *wdog* | *watchdog* )     var="watchdog" ;;
+    *powerkey* )              var="cold,powerkey" ;;
+    *panic* | *kernel_panic*) var="kernel_panic" ;;
+    *thermal*)                var="shutdown,thermal" ;;
+    *s3_wakeup*)              var="warm,s3_wakeup" ;;
+    *hw_reset*)               var="hard,hw_reset" ;;
+    *bootloader*)             var="bootloader" ;;
+    *)                        var="reboot" ;;
+  esac
+  echo ${var}
+}
+
+[ "USAGE: validate_property <property>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_property() {
+  val="`adb shell getprop ${1} 2>&1`"
+  ret=`validate_reason "${val}"`
+  if [ "reboot" = "${ret}" ]; then
+    ret=`validate_reason "reboot,${val}"`
+  fi
+  echo ${ret}
+}
+
+#
+# Actual test frames
+#
+
+[ "USAGE: test_properties
+
+properties test
+- (wait until screen is up, boot has completed)
+- adb shell getprop ro.boot.bootreason (bootloader reason)
+- adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason (system reason)
+- NB: all should have a value that is compliant with our known set." ]
+test_properties() {
+  duration_test 1
+  wait_for_screen
+  retval=0
+  check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+  bootloader=""
+  # NB: this test could fail if performed _after_ optional_factory_reset test
+  # and will report
+  #  ERROR: expected "reboot" got ""
+  #        for Android property persist.sys.boot.reason
+  # following is mitigation for the persist.sys.boot.reason, skip it
+  if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
+    check_set="ro.boot.bootreason sys.boot.reason"
+    bootloader="bootloader"
+  fi
+  for prop in ${check_set}; do
+    reason=`validate_property ${prop}`
+    EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
+  done
+  # sys.boot.reason is last for a reason
+  report_bootstat_logs ${reason} ${bootloader}
+  return ${retval}
+}
+
+[ "USAGE: test_ota
+
+ota test
+- rm out/.kati_stamp-* out/build_date.txt out/build_number.txt
+- rm out/target/product/*/*/*.prop
+- rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates
+- m
+- NB: ro.build.date.utc should update
+- fastboot flashall
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report ota
+
+Decision to change the build itself rather than trick bootstat by
+rummaging through its data files was made." ]
+test_ota() {
+  duration_test ">300"
+  echo "      extended by build and flashing times" >&2
+  if [ -z "${TARGET_PRODUCT}" -o \
+       -z "${ANDROID_PRODUCT_OUT}" -o \
+       -z "${ANDROID_BUILD_TOP}" -o \
+       -z "${TARGET_BUILD_VARIANT}" ]; then
+    echo "ERROR: Missing envsetup.sh and lunch" >&2
+    return 1
+  fi
+  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* ||
+    true
+  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt ||
+    true
+  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt ||
+    true
+  rm ${ANDROID_PRODUCT_OUT}/*/*.prop ||
+    true
+  rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
+    true
+  pushd ${ANDROID_BUILD_TOP} >&2
+  make -j50 >&2
+  if [ ${?} != 0 ]; then
+    popd >&2
+    return 1
+  fi
+  if ! inFastboot; then
+    adb reboot-bootloader >&2
+  fi
+  fastboot flashall >&2
+  popd >&2
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
+  EXPECT_PROPERTY persist.sys.boot.reason bootloader
+  report_bootstat_logs reboot,ota bootloader
+}
+
+[ "USAGE: test_optional_ota
+
+fast and fake (touch build_date on device to make it different)" ]
+test_optional_ota() {
+  checkDebugBuild || return
+  duration_test
+  adb shell su root touch /data/misc/bootstat/build_date >&2
+  adb reboot ota
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,ota
+  EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+  report_bootstat_logs reboot,ota
+}
+
+[ "USAGE: [TEST=<test>] blind_reboot_test
+
+Simple tests helper
+- adb reboot <test>
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report <test>, or reboot,<test> depending on canonical rules
+
+We interleave the simple reboot tests between the hard/complex ones
+as a means of checking sanity and any persistent side effect of the
+other tests." ]
+blind_reboot_test() {
+  duration_test
+  case ${TEST} in
+    bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
+    *)                                           reason=reboot,${TEST} ;;
+  esac
+  adb reboot ${TEST}
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason ${reason}
+  EXPECT_PROPERTY persist.sys.boot.reason ${reason}
+  report_bootstat_logs ${reason}
+}
+
+[ "USAGE: test_cold
+
+cold test
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report cold" ]
+test_cold() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_factory_reset
+
+factory_reset test
+- adb shell su root rm /data/misc/bootstat/build_date
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+Decision to rummage through bootstat data files was made as
+a _real_ factory_reset is too destructive to the device." ]
+test_factory_reset() {
+  checkDebugBuild || return
+  duration_test
+  adb shell su root rm /data/misc/bootstat/build_date >&2
+  adb reboot >&2
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+  EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+  report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
+    "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
+}
+
+[ "USAGE: test_optional_factory_reset
+
+factory_reset test
+- adb reboot-bootloader
+- fastboot format userdata
+- fastboot reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+For realz, and disruptive" ]
+test_optional_factory_reset() {
+  duration_test 60
+  if ! inFastboot; then
+    adb reboot-bootloader
+  fi
+  fastboot format userdata >&2
+  fastboot reboot >&2
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+  EXPECT_PROPERTY persist.sys.boot.reason ""
+  report_bootstat_logs reboot,factory_reset bootloader \
+    "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
+    "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" \
+    "-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset"
+}
+
+[ "USAGE: test_hard
+
+hard test:
+- adb reboot hard
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report hard" ]
+test_hard() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_battery
+
+battery test (trick):
+- echo healthd: battery l=2<space> | adb shell su root tee /dev/kmsg
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery, unless healthd managed to log
+  before reboot in above trick.
+
+- Bonus points (manual extras)
+- Make sure the following is added to the /init.rc file in post-fs
+  section before logd is started:
+    +    setprop logd.kernel false
+    +    rm /sys/fs/pstore/console-ramoops
+    +    rm /sys/fs/pstore/console-ramoops-0
+    +    write /dev/kmsg \"healthd: battery l=2${SPACE}
+    +\"
+- adb reboot fs
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery
+- (replace set logd.kernel true to the above, and retry test)" ]
+test_battery() {
+  checkDebugBuild || return
+  duration_test 120
+  enterPstore
+  # Send it _many_ times to combat devices with flakey pstore
+  for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
+    echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+  done
+  adb reboot cold >&2
+  adb wait-for-device
+  wait_for_screen
+  adb shell su root \
+    cat /proc/fs/pstore/console-ramoops \
+        /proc/fs/pstore/console-ramoops-0 2>/dev/null |
+    grep 'healthd: battery l=' |
+    tail -1 |
+    grep 'healthd: battery l=2 ' >/dev/null || (
+      if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
+        # retry
+        for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
+          echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+        done
+        adb reboot cold >&2
+        adb wait-for-device
+        wait_for_screen
+      fi
+    )
+
+  EXPECT_PROPERTY sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY persist.sys.boot.reason cold
+  report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
+  exitPstore
+}
+
+[ "USAGE: test_optional_battery
+
+battery shutdown test:
+- adb shell setprop sys.powerctl shutdown,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,battery" ]
+test_optional_battery() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+  report_bootstat_logs shutdown,battery
+}
+
+[ "USAGE: test_optional_battery_thermal
+
+battery thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal,battery" ]
+test_optional_battery_thermal() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,thermal,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+  report_bootstat_logs shutdown,thermal,battery
+}
+
+[ "USAGE: test_unknown
+
+unknown test
+- adb reboot unknown
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,unknown
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
+test_unknown() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_kernel_panic
+
+kernel_panic test:
+- echo c | adb shell su root tee /proc/sysrq-trigger
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report kernel_panic,sysrq" ]
+test_kernel_panic() {
+  checkDebugBuild || return
+  duration_test ">90"
+  panic_msg="kernel_panic,sysrq"
+  enterPstore || panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
+  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+  report_bootstat_logs kernel_panic,sysrq
+  exitPstore
+}
+
+[ "USAGE: test_warm
+
+warm test
+- adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report warm" ]
+test_warm() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_thermal_shutdown
+
+thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal" ]
+test_thermal_shutdown() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,thermal
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,thermal
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+  report_bootstat_logs shutdown,thermal
+}
+
+[ "USAGE: test_userrequested_shutdown
+
+userrequested shutdown test:
+- adb shell setprop sys.powerctl shutdown,userrequested
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,userrequested" ]
+test_userrequested_shutdown() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,userrequested
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+  report_bootstat_logs shutdown,userrequested
+}
+
+[ "USAGE: test_shell_reboot
+
+shell reboot test:
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,shell" ]
+test_shell_reboot() {
+  duration_test
+  adb shell reboot
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,shell
+  EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+  report_bootstat_logs reboot,shell
+}
+
+[ "USAGE: test_adb_reboot
+
+adb reboot test:
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,adb" ]
+test_adb_reboot() {
+  duration_test
+  adb reboot
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,adb
+  EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+  report_bootstat_logs reboot,adb
+}
+
+[ "USAGE: test_Its_Just_So_Hard_reboot
+
+Its Just So Hard reboot test:
+- adb shell reboot 'Its Just So Hard'
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,its_just_so_hard
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
+test_Its_Just_So_Hard_reboot() {
+  duration_test
+  adb shell 'reboot "Its Just So Hard"'
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
+  EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
+  adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
+  if checkDebugBuild; then
+    flag=""
+  else
+    flag="--allow_failure"
+  fi
+  EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
+  report_bootstat_logs reboot,its_just_so_hard
+}
+
+[ "USAGE: run_bootloader [value [expected]]
+
+bootloader boot reason injection tests:
+- setBootloaderBootReason value
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,value" ]
+run_bootloader() {
+  bootloader_expected="${1}"
+  if [ -z "${bootloader_expected}" ]; then
+    bootloader_expected="${TEST#bootloader_}"
+  fi
+  if ! setBootloaderBootReason ${bootloader_expected}; then
+    echo "       Skipping FAILURE." 2>&1
+    return
+  fi
+  duration_test
+  if [ X"warm" = X"${bootloader_expected}" ]; then
+    last_expected=cold
+  else
+    last_expected=warm
+  fi
+  adb reboot ${last_expected}
+  wait_for_screen
+  # Reset so that other tests do not get unexpected injection
+  setBootloaderBootReason
+  # Determine the expected values
+  sys_expected="${2}"
+  if [ -z "${sys_expected}" ]; then
+    sys_expected="`validate_reason ${bootloader_expected}`"
+    if [ "reboot" = "${sys_expected}" ]; then
+      sys_expected="${last_expected}"
+    fi
+  else
+    sys_expected=`validate_reason ${sys_expected}`
+  fi
+  case ${sys_expected} in
+    kernel_panic | kernel_panic,* | watchdog | watchdog,* )
+      last_expected=${sys_expected}
+      ;;
+  esac
+  # Check values
+  EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
+  EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+  EXPECT_PROPERTY persist.sys.boot.reason "${last_expected}"
+  report_bootstat_logs "${sys_expected}"
+}
+
+[ "USAGE: test_bootloader_<type>
+
+bootloader boot reasons test injection" ]
+test_bootloader_normal() {
+  run_bootloader
+}
+
+test_bootloader_watchdog() {
+  run_bootloader
+}
+
+test_bootloader_kernel_panic() {
+  run_bootloader
+}
+
+test_bootloader_oem_powerkey() {
+  run_bootloader
+}
+
+test_bootloader_wdog_reset() {
+  run_bootloader
+}
+
+test_bootloader_cold() {
+  run_bootloader
+}
+
+test_bootloader_warm() {
+  run_bootloader
+}
+
+test_bootloader_hard() {
+  run_bootloader
+}
+
+test_bootloader_recovery() {
+  run_bootloader
+}
+
+[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+
+Mainline executive to run the above tests" ]
+
+# Rudimentary argument parsing
+
+if [ ${#} -ge 2 -a X"-s" = X"${1}" ]; then
+  export ANDROID_SERIAL="${2}"
+  shift 2
+fi
+
+if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+  echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+  echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+  exit 0
+fi
+
+# Check if all conditions for the script are sane
+
+if [ -z "${ANDROID_SERIAL}" ]; then
+  ndev=`(
+      adb devices | grep -v 'List of devices attached'
+      fastboot devices
+    ) |
+    grep -v "^[${SPACE}${TAB}]*\$" |
+    wc -l`
+  if [ ${ndev} -gt 1 ]; then
+    echo "ERROR: no target device specified, ${ndev} connected" >&2
+    echo "${RED}[  FAILED  ]${NORMAL}"
+    exit 1
+  fi
+  echo "WARNING: no target device specified" >&2
+fi
+
+ret=0
+
+# Test Series
+if [ X"all" = X"${*}" ]; then
+  # automagically pick up all test_<function>s.
+  eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+  if [ X"nothing" = X"${1}" ]; then
+    shift 1
+  fi
+fi
+if [ -z "$*" ]; then
+  # automagically pick up all test_<function>, except test_optional_<function>.
+  eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
+                            grep -v '^optional_'`
+  if [ -z "${2}" ]; then
+    # Hard coded should shell fail to find them above (search/permission issues)
+    eval set properties ota cold factory_reset hard battery unknown \
+             kernel_panic warm thermal_shutdown userrequested_shutdown \
+             shell_reboot adb_reboot Its_Just_So_Hard_reboot \
+             bootloader_normal bootloader_watchdog bootloader_kernel_panic \
+             bootloader_oem_powerkey bootloader_wdog_reset \
+             bootloader_wdog_reset bootloader_wdog_reset bootloader_hard \
+             bootloader_recovery
+  fi
+  if [ X"nothing" = X"${1}" ]; then
+    shift 1
+  fi
+fi
+echo "INFO: selected test(s): ${@}" >&2
+echo
+# Prepare device
+setBootloaderBootReason 2>/dev/null
+# Start pouring through the tests.
+failures=
+successes=
+for t in "${@}"; do
+  wrap_test ${t}
+  retval=${?}
+  if [ 0 = ${retval} ]; then
+    if [ -z "${successes}" ]; then
+      successes=${t}
+    else
+      successes="${successes} ${t}"
+    fi
+  else
+    ret=${retval}
+    if [ -z "${failures}" ]; then
+      failures=${t}
+    else
+      failures="${failures} ${t}"
+    fi
+  fi
+  echo
+done
+
+if [ -n "${successes}" ]; then
+  echo "${GREEN}[  PASSED  ]${NORMAL} ${successes}"
+fi
+if [ -n "${failures}" ]; then
+  echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
+fi
+exit ${ret}
diff --git a/bootstat/bootstat-debug.rc b/bootstat/bootstat-debug.rc
new file mode 100644
index 0000000..6a00440
--- /dev/null
+++ b/bootstat/bootstat-debug.rc
@@ -0,0 +1,7 @@
+# This file is the userdebug LOCAL_INIT_RC file for the bootstat command.
+
+# FOR TESTING
+# For devices w/o bootloader boot reason reported, mirror test boot reason
+# to bootloader boot reason to allow test to inject reasons
+on property:persist.test.boot.reason=*
+    setprop ro.boot.bootreason ${persist.test.boot.reason}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a4c2160..8916c59 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -19,6 +19,7 @@
 // uploaded to Android log storage via Tron.
 
 #include <getopt.h>
+#include <sys/klog.h>
 #include <unistd.h>
 
 #include <chrono>
@@ -32,11 +33,14 @@
 #include <vector>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android/log.h>
+#include <cutils/android_reboot.h>
 #include <cutils/properties.h>
+#include <log/logcat.h>
 #include <metricslogger/metrics_logger.h>
 
 #include "boot_event_record_store.h"
@@ -57,8 +61,7 @@
 // Records the named boot |event| to the record store. If |value| is non-empty
 // and is a proper string representation of an integer value, the converted
 // integer value is associated with the boot event.
-void RecordBootEventFromCommandLine(
-    const std::string& event, const std::string& value_str) {
+void RecordBootEventFromCommandLine(const std::string& event, const std::string& value_str) {
   BootEventRecordStore boot_event_store;
   if (!value_str.empty()) {
     int32_t value = 0;
@@ -81,22 +84,23 @@
   }
 }
 
-void ShowHelp(const char *cmd) {
+void ShowHelp(const char* cmd) {
   fprintf(stderr, "Usage: %s [options]\n", cmd);
   fprintf(stderr,
           "options include:\n"
-          "  -h, --help            Show this help\n"
-          "  -l, --log             Log all metrics to logstorage\n"
-          "  -p, --print           Dump the boot event records to the console\n"
-          "  -r, --record          Record the timestamp of a named boot event\n"
-          "  --value               Optional value to associate with the boot event\n"
-          "  --record_boot_reason  Record the reason why the device booted\n"
+          "  -h, --help              Show this help\n"
+          "  -l, --log               Log all metrics to logstorage\n"
+          "  -p, --print             Dump the boot event records to the console\n"
+          "  -r, --record            Record the timestamp of a named boot event\n"
+          "  --value                 Optional value to associate with the boot event\n"
+          "  --record_boot_complete  Record metrics related to the time for the device boot\n"
+          "  --record_boot_reason    Record the reason why the device booted\n"
           "  --record_time_since_factory_reset Record the time since the device was reset\n");
 }
 
 // Constructs a readable, printable string from the givencommand line
 // arguments.
-std::string GetCommandLine(int argc, char **argv) {
+std::string GetCommandLine(int argc, char** argv) {
   std::string cmd;
   for (int i = 0; i < argc; ++i) {
     cmd += argv[i];
@@ -117,6 +121,15 @@
   return std::string(&temp[0], len);
 }
 
+void SetProperty(const char* key, const std::string& val) {
+  property_set(key, val.c_str());
+}
+
+void SetProperty(const char* key, const char* val) {
+  property_set(key, val);
+}
+
+constexpr int32_t kEmptyBootReason = 0;
 constexpr int32_t kUnknownBootReason = 1;
 
 // A mapping from boot reason string, as read from the ro.boot.bootreason
@@ -124,50 +137,81 @@
 // the boot_reason metric may refer to this mapping to discern the histogram
 // values.
 const std::map<std::string, int32_t> kBootReasonMap = {
-  {"unknown", kUnknownBootReason},
-  {"normal", 2},
-  {"recovery", 3},
-  {"reboot", 4},
-  {"PowerKey", 5},
-  {"hard_reset", 6},
-  {"kernel_panic", 7},
-  {"rpm_err", 8},
-  {"hw_reset", 9},
-  {"tz_err", 10},
-  {"adsp_err", 11},
-  {"modem_err", 12},
-  {"mba_err", 13},
-  {"Watchdog", 14},
-  {"Panic", 15},
-  {"power_key", 16},
-  {"power_on", 17},
-  {"Reboot", 18},
-  {"rtc", 19},
-  {"edl", 20},
-  {"oem_pon1", 21},
-  {"oem_powerkey", 22},
-  {"oem_unknown_reset", 23},
-  {"srto: HWWDT reset SC", 24},
-  {"srto: HWWDT reset platform", 25},
-  {"srto: bootloader", 26},
-  {"srto: kernel panic", 27},
-  {"srto: kernel watchdog reset", 28},
-  {"srto: normal", 29},
-  {"srto: reboot", 30},
-  {"srto: reboot-bootloader", 31},
-  {"srto: security watchdog reset", 32},
-  {"srto: wakesrc", 33},
-  {"srto: watchdog", 34},
-  {"srto:1-1", 35},
-  {"srto:omap_hsmm", 36},
-  {"srto:phy0", 37},
-  {"srto:rtc0", 38},
-  {"srto:touchpad", 39},
-  {"watchdog", 40},
-  {"watchdogr", 41},
-  {"wdog_bark", 42},
-  {"wdog_bite", 43},
-  {"wdog_reset", 44},
+    {"empty", kEmptyBootReason},
+    {"unknown", kUnknownBootReason},
+    {"normal", 2},
+    {"recovery", 3},
+    {"reboot", 4},
+    {"PowerKey", 5},
+    {"hard_reset", 6},
+    {"kernel_panic", 7},
+    {"rpm_err", 8},
+    {"hw_reset", 9},
+    {"tz_err", 10},
+    {"adsp_err", 11},
+    {"modem_err", 12},
+    {"mba_err", 13},
+    {"Watchdog", 14},
+    {"Panic", 15},
+    {"power_key", 16},
+    {"power_on", 17},
+    {"Reboot", 18},
+    {"rtc", 19},
+    {"edl", 20},
+    {"oem_pon1", 21},
+    {"oem_powerkey", 22},
+    {"oem_unknown_reset", 23},
+    {"srto: HWWDT reset SC", 24},
+    {"srto: HWWDT reset platform", 25},
+    {"srto: bootloader", 26},
+    {"srto: kernel panic", 27},
+    {"srto: kernel watchdog reset", 28},
+    {"srto: normal", 29},
+    {"srto: reboot", 30},
+    {"srto: reboot-bootloader", 31},
+    {"srto: security watchdog reset", 32},
+    {"srto: wakesrc", 33},
+    {"srto: watchdog", 34},
+    {"srto:1-1", 35},
+    {"srto:omap_hsmm", 36},
+    {"srto:phy0", 37},
+    {"srto:rtc0", 38},
+    {"srto:touchpad", 39},
+    {"watchdog", 40},
+    {"watchdogr", 41},
+    {"wdog_bark", 42},
+    {"wdog_bite", 43},
+    {"wdog_reset", 44},
+    {"shutdown,", 45},  // Trailing comma is intentional.
+    {"shutdown,userrequested", 46},
+    {"reboot,bootloader", 47},
+    {"reboot,cold", 48},
+    {"reboot,recovery", 49},
+    {"thermal_shutdown", 50},
+    {"s3_wakeup", 51},
+    {"kernel_panic,sysrq", 52},
+    {"kernel_panic,NULL", 53},
+    {"kernel_panic,BUG", 54},
+    {"bootloader", 55},
+    {"cold", 56},
+    {"hard", 57},
+    {"warm", 58},
+    {"recovery", 59},
+    {"thermal-shutdown", 60},
+    {"shutdown,thermal", 61},
+    {"shutdown,battery", 62},
+    {"reboot,ota", 63},
+    {"reboot,factory_reset", 64},
+    {"reboot,", 65},
+    {"reboot,shell", 66},
+    {"reboot,adb", 67},
+    {"reboot,userrequested", 68},
+    {"shutdown,container", 69},  // Host OS asking Android Container to shutdown
+    {"cold,powerkey", 70},
+    {"warm,s3_wakeup", 71},
+    {"hard,hw_reset", 72},
+    {"shutdown,suspend", 73},    // Suspend to RAM
+    {"shutdown,hibernate", 74},  // Suspend to DISK
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -179,10 +223,372 @@
     return mapping->second;
   }
 
+  if (boot_reason.empty()) {
+    return kEmptyBootReason;
+  }
+
   LOG(INFO) << "Unknown boot reason: " << boot_reason;
   return kUnknownBootReason;
 }
 
+// Canonical list of supported primary reboot reasons.
+const std::vector<const std::string> knownReasons = {
+    // clang-format off
+    // kernel
+    "watchdog",
+    "kernel_panic",
+    // strong
+    "recovery",    // Should not happen from ro.boot.bootreason
+    "bootloader",  // Should not happen from ro.boot.bootreason
+    // blunt
+    "cold",
+    "hard",
+    "warm",
+    // super blunt
+    "shutdown",    // Can not happen from ro.boot.bootreason
+    "reboot",      // Default catch-all for anything unknown
+    // clang-format on
+};
+
+// Returns true if the supplied reason prefix is considered detailed enough.
+bool isStrongRebootReason(const std::string& r) {
+  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()] == ','))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Returns true if the supplied reason prefix is associated with the kernel.
+bool isKernelRebootReason(const std::string& r) {
+  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()] == ','))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Returns true if the supplied reason prefix is considered known.
+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()] == ','))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// If the reboot reason should be improved, report true if is too blunt.
+bool isBluntRebootReason(const std::string& r) {
+  if (isStrongRebootReason(r)) return false;
+
+  if (!isKnownRebootReason(r)) return true;  // Can not support unknown as detail
+
+  size_t pos = 0;
+  while ((pos = r.find(',', pos)) != std::string::npos) {
+    ++pos;
+    std::string next(r.substr(pos));
+    if (next.length() == 0) break;
+    if (next[0] == ',') continue;
+    if (!isKnownRebootReason(next)) return false;  // Unknown subreason is good.
+    if (isStrongRebootReason(next)) return false;  // eg: reboot,reboot
+  }
+  return true;
+}
+
+bool readPstoreConsole(std::string& console) {
+  if (android::base::ReadFileToString("/sys/fs/pstore/console-ramoops-0", &console)) {
+    return true;
+  }
+  return android::base::ReadFileToString("/sys/fs/pstore/console-ramoops", &console);
+}
+
+bool addKernelPanicSubReason(const std::string& console, std::string& ret) {
+  // Check for kernel panic types to refine information
+  if (console.rfind("SysRq : Trigger a crash") != std::string::npos) {
+    // Can not happen, except on userdebug, during testing/debugging.
+    ret = "kernel_panic,sysrq";
+    return true;
+  }
+  if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") !=
+      std::string::npos) {
+    ret = "kernel_panic,NULL";
+    return true;
+  }
+  if (console.rfind("Kernel BUG at ") != std::string::npos) {
+    ret = "kernel_panic,BUG";
+    return true;
+  }
+  return false;
+}
+
+// std::transform Helper callback functions:
+// Converts a string value representing the reason the system booted to a
+// string complying with Android system standard reason.
+char tounderline(char c) {
+  return ::isblank(c) ? '_' : c;
+}
+char toprintable(char c) {
+  return ::isprint(c) ? c : '?';
+}
+
+const char system_reboot_reason_property[] = "sys.boot.reason";
+const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
+
+// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
+std::string BootReasonStrToReason(const std::string& boot_reason) {
+  static const size_t max_reason_length = 256;
+
+  std::string ret(GetProperty(system_reboot_reason_property));
+  std::string reason(boot_reason);
+  // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
+  if (reason == ret) ret = "";
+
+  // Cleanup boot_reason regarding acceptable character set
+  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+  std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
+  std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+
+  // Is the current system boot reason sys.boot.reason valid?
+  if (!isKnownRebootReason(ret)) ret = "";
+
+  if (ret == "") {
+    // Is the bootloader boot reason ro.boot.bootreason known?
+    std::vector<std::string> words(android::base::Split(reason, ",_-"));
+    for (auto& s : knownReasons) {
+      std::string blunt;
+      for (auto& r : words) {
+        if (r == s) {
+          if (isBluntRebootReason(s)) {
+            blunt = s;
+          } else {
+            ret = s;
+            break;
+          }
+        }
+      }
+      if (ret == "") ret = blunt;
+      if (ret != "") break;
+    }
+  }
+
+  if (ret == "") {
+    // A series of checks to take some officially unsupported reasons
+    // reported by the bootloader and find some logical and canonical
+    // sense.  In an ideal world, we would require those bootloaders
+    // to behave and follow our standards.
+    static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
+        {"watchdog", "wdog"},
+        {"cold,powerkey", "powerkey"},
+        {"kernel_panic", "panic"},
+        {"shutdown,thermal", "thermal"},
+        {"warm,s3_wakeup", "s3_wakeup"},
+        {"hard,hw_reset", "hw_reset"},
+        {"bootloader", ""},
+    };
+
+    // Either the primary or alias is found _somewhere_ in the reason string.
+    for (auto& s : aliasReasons) {
+      if (reason.find(s.first) != std::string::npos) {
+        ret = s.first;
+        break;
+      }
+      if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
+        ret = s.first;
+        break;
+      }
+    }
+  }
+
+  // If watchdog is the reason, see if there is a security angle?
+  if (ret == "watchdog") {
+    if (reason.find("sec") != std::string::npos) {
+      ret += ",security";
+    }
+  }
+
+  if (ret == "kernel_panic") {
+    // Check to see if last klog has some refinement hints.
+    std::string content;
+    if (readPstoreConsole(content)) {
+      addKernelPanicSubReason(content, ret);
+    }
+  } else if (isBluntRebootReason(ret)) {
+    // Check the other available reason resources if the reason is still blunt.
+
+    // Check to see if last klog has some refinement hints.
+    std::string content;
+    if (readPstoreConsole(content)) {
+      // The toybox reboot command used directly (unlikely)? But also
+      // catches init's response to Android's more controlled reboot command.
+      if (content.rfind("reboot: Power down") != std::string::npos) {
+        ret = "shutdown";  // Still too blunt, but more accurate.
+        // ToDo: init should record the shutdown reason to kernel messages ala:
+        //           init: shutdown system with command 'last_reboot_reason'
+        //       so that if pstore has persistence we can get some details
+        //       that could be missing in last_reboot_reason_property.
+      }
+
+      static const char cmd[] = "reboot: Restarting system with command '";
+      size_t pos = content.rfind(cmd);
+      if (pos != std::string::npos) {
+        pos += strlen(cmd);
+        std::string subReason(content.substr(pos, max_reason_length));
+        for (pos = 0; pos < subReason.length(); ++pos) {
+          char c = tounderline(subReason[pos]);
+          if (!::isprint(c) || (c == '\'')) {
+            subReason.erase(pos);
+            break;
+          }
+          subReason[pos] = ::tolower(c);
+        }
+        if (subReason != "") {  // Will not land "reboot" as that is too blunt.
+          if (isKernelRebootReason(subReason)) {
+            ret = "reboot," + subReason;  // User space can't talk kernel reasons.
+          } else {
+            ret = subReason;
+          }
+        }
+      }
+
+      // Check for kernel panics, allowed to override reboot command.
+      if (!addKernelPanicSubReason(content, ret) &&
+          // check for long-press power down
+          ((content.rfind("Power held for ") != std::string::npos) ||
+           (content.rfind("charger: [") != std::string::npos))) {
+        ret = "cold";
+      }
+    }
+
+    // The following battery test should migrate to a default system health HAL
+
+    // Let us not worry if the reboot command was issued, for the cases of
+    // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
+    // Same for bootloader and ro.boot.bootreasons of this set, but a dead
+    // battery could conceivably lead to these, so worthy of override.
+    if (isBluntRebootReason(ret)) {
+      // Heuristic to determine if shutdown possibly because of a dead battery?
+      // Really a hail-mary pass to find it in last klog content ...
+      static const int battery_dead_threshold = 2;  // percent
+      static const char battery[] = "healthd: battery l=";
+      size_t pos = content.rfind(battery);  // last one
+      std::string digits;
+      if (pos != std::string::npos) {
+        digits = content.substr(pos + strlen(battery));
+      }
+      char* endptr = NULL;
+      unsigned long long level = strtoull(digits.c_str(), &endptr, 10);
+      if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
+        LOG(INFO) << "Battery level at shutdown " << level << "%";
+        if (level <= battery_dead_threshold) {
+          ret = "shutdown,battery";
+        }
+      } else {        // Most likely
+        digits = "";  // reset digits
+
+        // Content buffer no longer will have console data. Beware if more
+        // checks added below, that depend on parsing console content.
+        content = "";
+
+        LOG(DEBUG) << "Can not find last low battery in last console messages";
+        android_logcat_context ctx = create_android_logcat();
+        FILE* fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
+        if (fp != nullptr) {
+          android::base::ReadFdToString(fileno(fp), &content);
+        }
+        android_logcat_pclose(&ctx, fp);
+        static const char logcat_battery[] = "W/healthd (    0): battery l=";
+        const char* match = logcat_battery;
+
+        if (content == "") {
+          // Service logd.klog not running, go to smaller buffer in the kernel.
+          int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
+          if (rc > 0) {
+            ssize_t len = rc + 1024;  // 1K Margin should it grow between calls.
+            std::unique_ptr<char[]> buf(new char[len]);
+            rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+            if (rc < len) {
+              len = rc + 1;
+            }
+            buf[--len] = '\0';
+            content = buf.get();
+          }
+          match = battery;
+        }
+
+        pos = content.find(match);  // The first one it finds.
+        if (pos != std::string::npos) {
+          digits = content.substr(pos + strlen(match));
+        }
+        endptr = NULL;
+        level = strtoull(digits.c_str(), &endptr, 10);
+        if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
+          LOG(INFO) << "Battery level at startup " << level << "%";
+          if (level <= battery_dead_threshold) {
+            ret = "shutdown,battery";
+          }
+        } else {
+          LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
+        }
+      }
+    }
+
+    // Is there a controlled shutdown hint in last_reboot_reason_property?
+    if (isBluntRebootReason(ret)) {
+      // Content buffer no longer will have console data. Beware if more
+      // checks added below, that depend on parsing console content.
+      content = GetProperty(last_reboot_reason_property);
+      // Cleanup last_boot_reason regarding acceptable character set
+      std::transform(content.begin(), content.end(), content.begin(), ::tolower);
+      std::transform(content.begin(), content.end(), content.begin(), tounderline);
+      std::transform(content.begin(), content.end(), content.begin(), toprintable);
+
+      // Anything in last is better than 'super-blunt' reboot or shutdown.
+      if ((ret == "") || (ret == "reboot") || (ret == "shutdown") || !isBluntRebootReason(content)) {
+        ret = content;
+      }
+    }
+
+    // Other System Health HAL reasons?
+
+    // ToDo: /proc/sys/kernel/boot_reason needs a HAL interface to
+    //       possibly offer hardware-specific clues from the PMIC.
+  }
+
+  // If unknown left over from above, make it "reboot,<boot_reason>"
+  if (ret == "") {
+    ret = "reboot";
+    if (android::base::StartsWith(reason, "reboot")) {
+      reason = reason.substr(strlen("reboot"));
+      while ((reason[0] == ',') || (reason[0] == '_')) {
+        reason = reason.substr(1);
+      }
+    }
+    if (reason != "") {
+      ret += ",";
+      ret += reason;
+    }
+  }
+
+  LOG(INFO) << "Canonical boot reason: " << ret;
+  if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
+    // Rewrite as it must be old news, kernel reasons trump user space.
+    SetProperty(last_reboot_reason_property, ret);
+  }
+  return ret;
+}
+
 // Returns the appropriate metric key prefix for the boot_complete metric such
 // that boot metrics after a system update are labeled as ota_boot_complete;
 // otherwise, they are labeled as boot_complete.  This method encapsulates the
@@ -204,17 +610,20 @@
   if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
     boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+    LOG(INFO) << "Canonical boot reason: reboot,factory_reset";
+    SetProperty(system_reboot_reason_property, "reboot,factory_reset");
   } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+    LOG(INFO) << "Canonical boot reason: reboot,ota";
+    SetProperty(system_reboot_reason_property, "reboot,ota");
   }
 
   return boot_complete_prefix;
 }
 
 // Records the value of a given ro.boottime.init property in milliseconds.
-void RecordInitBootTimeProp(
-    BootEventRecordStore* boot_event_store, const char* property) {
+void RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) {
   std::string value = GetProperty(property);
 
   int32_t time_in_ms;
@@ -299,10 +708,8 @@
 
   if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
     time_t last_boot_time_utc = record.second;
-    time_t time_since_last_boot = difftime(current_time_utc,
-                                           last_boot_time_utc);
-    boot_event_store.AddBootEventWithValue("time_since_last_boot",
-                                           time_since_last_boot);
+    time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
+    boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot);
   }
 
   boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc);
@@ -328,8 +735,7 @@
     boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
                                            boot_complete.count());
   } else {
-      boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
-                                             uptime.count());
+    boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", uptime.count());
   }
 
   // Record the total time from device startup to boot complete, regardless of
@@ -350,9 +756,33 @@
 // Records the boot_reason metric by querying the ro.boot.bootreason system
 // property.
 void RecordBootReason() {
-  int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+  const std::string reason(GetProperty(bootloader_reboot_reason_property));
+
+  if (reason.empty()) {
+    // Log an empty boot reason value as '<EMPTY>' to ensure the value is intentional
+    // (and not corruption anywhere else in the reporting pipeline).
+    android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+                                           android::metricslogger::FIELD_PLATFORM_REASON, "<EMPTY>");
+  } else {
+    android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+                                           android::metricslogger::FIELD_PLATFORM_REASON, reason);
+  }
+
+  // Log the raw bootloader_boot_reason property value.
+  int32_t boot_reason = BootReasonStrToEnum(reason);
   BootEventRecordStore boot_event_store;
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+
+  // Log the scrubbed system_boot_reason.
+  const std::string system_reason(BootReasonStrToReason(reason));
+  int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
+  boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
+
+  // Record the scrubbed system_boot_reason to the property
+  SetProperty(system_reboot_reason_property, system_reason);
+  if (reason == "") {
+    SetProperty(bootloader_reboot_reason_property, system_reason);
+  }
 }
 
 // Records two metrics related to the user resetting a device: the time at
@@ -366,21 +796,20 @@
 
   if (current_time_utc < 0) {
     // UMA does not display negative values in buckets, so convert to positive.
-    android::metricslogger::LogHistogram(
-        "factory_reset_current_time_failure", std::abs(current_time_utc));
+    android::metricslogger::LogHistogram("factory_reset_current_time_failure",
+                                         std::abs(current_time_utc));
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
-    boot_event_store.AddBootEventWithValue(
-        "factory_reset_current_time_failure", std::abs(current_time_utc));
+    boot_event_store.AddBootEventWithValue("factory_reset_current_time_failure",
+                                           std::abs(current_time_utc));
     return;
   } else {
     android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc);
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
-    boot_event_store.AddBootEventWithValue(
-        "factory_reset_current_time", current_time_utc);
+    boot_event_store.AddBootEventWithValue("factory_reset_current_time", current_time_utc);
   }
 
   // The factory_reset boot event does not exist after the device is reset, so
@@ -400,18 +829,15 @@
 
   // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
   // is losing records somehow.
-  boot_event_store.AddBootEventWithValue(
-      "factory_reset_record_value", factory_reset_utc);
+  boot_event_store.AddBootEventWithValue("factory_reset_record_value", factory_reset_utc);
 
-  time_t time_since_factory_reset = difftime(current_time_utc,
-                                             factory_reset_utc);
-  boot_event_store.AddBootEventWithValue("time_since_factory_reset",
-                                         time_since_factory_reset);
+  time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc);
+  boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
 }
 
 }  // namespace
 
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
   android::base::InitLogging(argv);
 
   const std::string cmd_line = GetCommandLine(argc, argv);
@@ -423,15 +849,17 @@
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
-    { "help",            no_argument,       NULL,   'h' },
-    { "log",             no_argument,       NULL,   'l' },
-    { "print",           no_argument,       NULL,   'p' },
-    { "record",          required_argument, NULL,   'r' },
-    { value_str,         required_argument, NULL,   0 },
-    { boot_complete_str, no_argument,       NULL,   0 },
-    { boot_reason_str,   no_argument,       NULL,   0 },
-    { factory_reset_str, no_argument,       NULL,   0 },
-    { NULL,              0,                 NULL,   0 }
+      // clang-format off
+      { "help",            no_argument,       NULL,   'h' },
+      { "log",             no_argument,       NULL,   'l' },
+      { "print",           no_argument,       NULL,   'p' },
+      { "record",          required_argument, NULL,   'r' },
+      { value_str,         required_argument, NULL,   0 },
+      { boot_complete_str, no_argument,       NULL,   0 },
+      { boot_reason_str,   no_argument,       NULL,   0 },
+      { factory_reset_str, no_argument,       NULL,   0 },
+      { NULL,              0,                 NULL,   0 }
+      // clang-format on
   };
 
   std::string boot_event;
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f4756d5..f06a38f 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,7 +1,43 @@
 # This file is the LOCAL_INIT_RC file for the bootstat command.
 
+# mirror bootloader boot reason to system boot reason
+on property:ro.boot.bootreason=*
+    setprop sys.boot.reason ${ro.boot.bootreason}
+
 on post-fs-data
-    mkdir /data/misc/bootstat 0700 root root
+    mkdir /data/misc/bootstat 0700 system log
+    # To deal with ota transition resulting from a change in DAC from
+    # root.root to system.log, may be deleted after ota has settled.
+    chown system log /data/misc/bootstat/absolute_boot_time
+    chown system log /data/misc/bootstat/boot_complete
+    chown system log /data/misc/bootstat/boot_complete_no_encryption
+    chown system log /data/misc/bootstat/boot_reason
+    chown system log /data/misc/bootstat/boottime.bootloader.1BLE
+    chown system log /data/misc/bootstat/boottime.bootloader.1BLL
+    chown system log /data/misc/bootstat/boottime.bootloader.2BLE
+    chown system log /data/misc/bootstat/boottime.bootloader.2BLL
+    chown system log /data/misc/bootstat/boottime.bootloader.AVB
+    chown system log /data/misc/bootstat/boottime.bootloader.KD
+    chown system log /data/misc/bootstat/boottime.bootloader.KL
+    chown system log /data/misc/bootstat/boottime.bootloader.ODT
+    chown system log /data/misc/bootstat/boottime.bootloader.SW
+    chown system log /data/misc/bootstat/boottime.bootloader.total
+    chown system log /data/misc/bootstat/build_date
+    chown system log /data/misc/bootstat/factory_reset
+    chown system log /data/misc/bootstat/factory_reset_boot_complete
+    chown system log /data/misc/bootstat/factory_reset_boot_complete_no_encryption
+    chown system log /data/misc/bootstat/factory_reset_current_time
+    chown system log /data/misc/bootstat/factory_reset_record_value
+    chown system log /data/misc/bootstat/last_boot_time_utc
+    chown system log /data/misc/bootstat/ota_boot_complete
+    chown system log /data/misc/bootstat/ota_boot_complete_no_encryption
+    chown system log /data/misc/bootstat/post_decrypt_time_elapsed
+    chown system log /data/misc/bootstat/ro.boottime.init
+    chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait
+    chown system log /data/misc/bootstat/ro.boottime.init.selinux
+    chown system log /data/misc/bootstat/time_since_factory_reset
+    chown system log /data/misc/bootstat/time_since_last_boot
+    # end ota transitional support
 
 # Record the time at which the user has successfully entered the pin to decrypt
 # the device, /data is decrypted, and the system is entering the main boot phase.
@@ -10,7 +46,7 @@
 # property:init.svc.bootanim=running: The boot animation is running
 # property:ro.crypto.type=block: FDE device
 on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
-    exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+    exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
 
 # sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
 # This signaling is necessary to prevent logging boot metrics after a runtime
@@ -33,13 +69,7 @@
 # Record boot complete metrics.
 on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
     # Record boot_complete and related stats (decryption, etc).
-    exec - root root -- /system/bin/bootstat --record_boot_complete
-
     # Record the boot reason.
-    exec - root root -- /system/bin/bootstat --record_boot_reason
-
     # Record time since factory reset.
-    exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
-
     # Log all boot events.
-    exec - root root -- /system/bin/bootstat -l
+    exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5565cfd..2b5f4f6 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -66,7 +66,10 @@
     defaults: ["debuggerd_defaults"],
     srcs: ["handler/debuggerd_handler.cpp"],
 
-    header_libs: ["libdebuggerd_common_headers"],
+    header_libs: [
+        "libbase_headers",
+        "libdebuggerd_common_headers",
+    ],
 
     whole_static_libs: [
         "libasync_safe",
@@ -106,6 +109,7 @@
         "libdebuggerd",
         "libbacktrace",
         "libunwind",
+        "libunwindstack",
         "liblzma",
         "libcutils",
     ],
@@ -171,6 +175,7 @@
     static_libs: [
         "libbacktrace",
         "libunwind",
+        "libunwindstack",
         "liblzma",
         "libbase",
         "libcutils",
@@ -274,7 +279,7 @@
         "libbase",
         "libdebuggerd_client",
         "liblog",
-        "libselinux",
+        "libprocinfo",
     ],
 
     local_include_dirs: ["include"],
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 8420f03..9c2f0d6 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -27,6 +27,7 @@
 #include <gtest/gtest.h>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -36,8 +37,20 @@
 using namespace std::chrono_literals;
 using android::base::unique_fd;
 
+static int getThreadCount() {
+  int threadCount = 1024;
+  std::vector<std::string> characteristics =
+      android::base::Split(android::base::GetProperty("ro.build.characteristics", ""), ",");
+  if (std::find(characteristics.begin(), characteristics.end(), "embedded")
+      != characteristics.end()) {
+    // 128 is the realistic number for iot devices.
+    threadCount = 128;
+  }
+  return threadCount;
+}
+
 TEST(debuggerd_client, race) {
-  static constexpr int THREAD_COUNT = 1024;
+  static int THREAD_COUNT = getThreadCount();
   pid_t forkpid = fork();
 
   ASSERT_NE(-1, forkpid);
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 5db0e5f..6ef3ed6 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -44,13 +44,17 @@
 #include <private/android_filesystem_config.h>
 #include <procinfo/process.h>
 
-#include "backtrace.h"
-#include "tombstone.h"
-#include "utility.h"
+#define ATRACE_TAG ATRACE_TAG_BIONIC
+#include <utils/Trace.h>
+
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
+#include "libdebuggerd/utility.h"
 
 #include "debuggerd/handler.h"
-#include "protocol.h"
 #include "tombstoned/tombstoned.h"
+
+#include "protocol.h"
 #include "util.h"
 
 using android::base::unique_fd;
@@ -76,9 +80,27 @@
   return fstatat(pid_proc_fd, task_path.c_str(), &st, 0) == 0;
 }
 
+static pid_t get_tracer(pid_t tracee) {
+  // Check to see if the thread is being ptraced by another process.
+  android::procinfo::ProcessInfo process_info;
+  if (android::procinfo::GetProcessInfo(tracee, &process_info)) {
+    return process_info.tracer;
+  }
+  return -1;
+}
+
 // 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) {
+    if (errno == EPERM) {
+      pid_t tracer = get_tracer(tid);
+      if (tracer != -1) {
+        *error = StringPrintf("failed to attach to thread %d, already traced by %d (%s)", tid,
+                              tracer, get_process_name(tracer).c_str());
+        return false;
+      }
+    }
+
     *error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
     return false;
   }
@@ -101,6 +123,7 @@
 }
 
 static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
+  ATRACE_CALL();
   android::base::unique_fd amfd(socket_local_client(
       "/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
   if (amfd.get() == -1) {
@@ -176,6 +199,7 @@
 }
 
 static void drop_capabilities() {
+  ATRACE_CALL();
   __user_cap_header_struct capheader;
   memset(&capheader, 0, sizeof(capheader));
   capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -194,6 +218,8 @@
 }
 
 int main(int argc, char** argv) {
+  atrace_begin(ATRACE_TAG, "before reparent");
+
   pid_t target = getppid();
   bool tombstoned_connected = false;
   unique_fd tombstoned_socket;
@@ -261,6 +287,8 @@
     PLOG(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.
   pid_t forkpid = fork();
@@ -270,6 +298,8 @@
     exit(0);
   }
 
+  ATRACE_NAME("after reparent");
+
   // Die if we take too long.
   //
   // Note: processes with many threads and minidebug-info can take a bit to
@@ -278,42 +308,58 @@
 
   std::string attach_error;
 
-  // Seize the main thread.
-  if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
-    LOG(FATAL) << attach_error;
-  }
-
-  // Seize the siblings.
   std::map<pid_t, std::string> threads;
+
   {
-    std::set<pid_t> siblings;
-    if (!android::procinfo::GetProcessTids(target, &siblings)) {
-      PLOG(FATAL) << "failed to get process siblings";
+    ATRACE_NAME("ptrace");
+    // Seize the main thread.
+    if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
+      LOG(FATAL) << attach_error;
     }
 
-    // but not the already attached main thread.
-    siblings.erase(main_tid);
-    // or the handler pseudothread.
-    siblings.erase(pseudothread_tid);
+    // Seize the siblings.
+    {
+      std::set<pid_t> siblings;
+      if (!android::procinfo::GetProcessTids(target, &siblings)) {
+        PLOG(FATAL) << "failed to get process siblings";
+      }
 
-    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));
+      // 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(BacktraceMap::Create(main_tid));
-  if (!backtrace_map) {
-    LOG(FATAL) << "failed to create backtrace map";
+  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";
+    }
+  }
+  std::unique_ptr<BacktraceMap> backtrace_map_new;
+  backtrace_map_new.reset(BacktraceMap::CreateNew(main_tid));
+  if (!backtrace_map_new) {
+    LOG(FATAL) << "failed to create backtrace map new";
   }
 
   // Collect the list of open files.
   OpenFilesList open_files;
-  populate_open_files_list(target, &open_files);
+  {
+    ATRACE_NAME("open files");
+    populate_open_files_list(target, &open_files);
+  }
 
   std::string process_name = get_process_name(main_tid);
   threads.emplace(main_tid, get_thread_name(main_tid));
@@ -321,9 +367,12 @@
   // Drop our capabilities now that we've attached to the threads we care about.
   drop_capabilities();
 
-  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);
+  {
+    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);
+  }
 
   // Write a '\1' to stdout to tell the crashing process to resume.
   // It also restores the value of PR_SET_DUMPABLE at this point.
@@ -352,9 +401,12 @@
   }
 
   siginfo_t siginfo = {};
-  if (!wait_for_signal(main_tid, &siginfo)) {
-    printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
-    exit(1);
+  {
+    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);
+    }
   }
 
   int signo = siginfo.si_signo;
@@ -376,10 +428,13 @@
 
   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);
   } else {
-    engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
-                      process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+    ATRACE_NAME("engrave_tombstone");
+    engrave_tombstone(output_fd.get(), backtrace_map.get(), backtrace_map_new.get(), &open_files,
+                      target, main_tid, process_name, threads, abort_address,
+                      fatal_signal ? &amfd_data : nullptr);
   }
 
   // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
@@ -407,14 +462,14 @@
   if (wait_for_gdb) {
     // Use ALOGI to line up with output from engrave_tombstone.
     ALOGI(
-      "***********************************************************\n"
-      "* Process %d has been suspended while crashing.\n"
-      "* To attach gdbserver and start gdb, run this on the host:\n"
-      "*\n"
-      "*     gdbclient.py -p %d\n"
-      "*\n"
-      "***********************************************************",
-      target, main_tid);
+        "***********************************************************\n"
+        "* Process %d has been suspended while crashing.\n"
+        "* To attach gdbserver and start gdb, run this on the host:\n"
+        "*\n"
+        "*     gdbclient.py -p %d\n"
+        "*\n"
+        "***********************************************************",
+        target, target);
   }
 
   if (fatal_signal) {
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index f73f672..67b4ab7 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -2,7 +2,6 @@
     name: "crasher-defaults",
 
     cppflags: [
-        "-std=gnu++14",
         "-W",
         "-Wall",
         "-Wextra",
@@ -18,7 +17,7 @@
         arm: {
             srcs: ["arm/crashglue.S"],
 
-            armv7_a_neon: {
+            neon: {
                 asflags: ["-DHAS_VFP_D32"],
             },
         },
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6970201..e9a3ebd 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -114,6 +114,11 @@
     return reinterpret_cast<uintptr_t>(result);
 }
 
+noinline int crash_null() {
+  int (*null_func)() = nullptr;
+  return null_func();
+}
+
 noinline int crash3(int a) {
     *reinterpret_cast<int*>(0xdead) = a;
     return a*4;
@@ -134,6 +139,14 @@
     free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
 }
 
+noinline void leak() {
+    while (true) {
+        void* mapping =
+            mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        static_cast<volatile char*>(mapping)[0] = 'a';
+    }
+}
+
 noinline void sigsegv_non_null() {
     int* a = (int *)(&do_action);
     *a = 42;
@@ -160,8 +173,9 @@
     fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
     fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
     fprintf(stderr, "\n");
-    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
     fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
+    fprintf(stderr, "  call-null             cause a crash by calling through a nullptr\n");
+    fprintf(stderr, "  leak                  leak memory until we get OOM-killed\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  abort                 call abort()\n");
     fprintf(stderr, "  assert                call assert() without a function\n");
@@ -231,6 +245,8 @@
         crashnostack();
     } else if (!strcasecmp(arg, "exit")) {
         exit(1);
+    } else if (!strcasecmp(arg, "call-null")) {
+      return crash_null();
     } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
         return crash(42);
     } else if (!strcasecmp(arg, "abort")) {
@@ -265,6 +281,8 @@
         return pthread_join(0, nullptr);
     } else if (!strcasecmp(arg, "heap-usage")) {
         abuse_heap();
+    } else if (!strcasecmp(arg, "leak")) {
+        leak();
     } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
         char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
                                                  MAP_SHARED | MAP_ANONYMOUS, -1, 0));
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 6298ace..b016e23 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -27,7 +27,7 @@
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <debuggerd/client.h>
-#include <selinux/selinux.h>
+#include <procinfo/process.h>
 #include "util.h"
 
 using android::base::unique_fd;
@@ -66,6 +66,24 @@
     usage(1);
   }
 
+  if (getuid() != 0) {
+    errx(1, "root is required");
+  }
+
+  // Check to see if the process exists and that we can actually send a signal to it.
+  android::procinfo::ProcessInfo proc_info;
+  if (!android::procinfo::GetProcessInfo(pid, &proc_info)) {
+    err(1, "failed to fetch info for process %d", pid);
+  }
+
+  if (proc_info.state == android::procinfo::kProcessStateZombie) {
+    errx(1, "process %d is a zombie", pid);
+  }
+
+  if (kill(pid, 0) != 0) {
+    err(1, "cannot send signal to process %d", pid);
+  }
+
   unique_fd piperead, pipewrite;
   if (!Pipe(&piperead, &pipewrite)) {
     err(1, "failed to create pipe");
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 2015157..45e768d 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -16,10 +16,12 @@
 
 #include <err.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <sys/capability.h>
 #include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <chrono>
 #include <regex>
@@ -88,8 +90,12 @@
     }                                                                                       \
   } 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"(\+)")
+
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
-                                 DebuggerdDumpType intercept_type) {
+                                 InterceptStatus* status, DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (intercept_fd->get() == -1) {
@@ -136,7 +142,7 @@
            << ", received " << rc;
   }
 
-  ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+  *status = response.status;
 }
 
 class CrasherTest : public ::testing::Test {
@@ -180,7 +186,9 @@
     FAIL() << "crasher hasn't been started";
   }
 
-  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type);
+  InterceptStatus status;
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -305,7 +313,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)");
+  ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
 TEST_F(CrasherTest, signal) {
@@ -441,7 +449,7 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+  /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+  ASSERT_BACKTRACE_FRAME(result, "read");
 
   int status;
   ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
@@ -452,7 +460,7 @@
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)");
+  ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
 TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
@@ -472,7 +480,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(abort)");
+  ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
 TEST_F(CrasherTest, capabilities) {
@@ -529,7 +537,7 @@
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
 TEST_F(CrasherTest, fake_pid) {
@@ -560,7 +568,41 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+  ASSERT_BACKTRACE_FRAME(result, "tgkill");
+}
+
+TEST_F(CrasherTest, competing_tracer) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    while (true) {
+    }
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+
+  ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0));
+  ASSERT_EQ(0, kill(crasher_pid, SIGABRT));
+
+  int status;
+  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_TRUE(WIFSTOPPED(status));
+  ASSERT_EQ(SIGABRT, WSTOPSIG(status));
+
+  ASSERT_EQ(0, ptrace(PTRACE_CONT, crasher_pid, 0, SIGABRT));
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  std::string regex = R"(failed to attach to thread \d+, already traced by )";
+  regex += std::to_string(gettid());
+  regex += R"( \(.+debuggerd_test)";
+  ASSERT_MATCH(result, regex.c_str());
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT));
+  AssertDeath(SIGABRT);
 }
 
 TEST(crash_dump, zombie) {
@@ -598,7 +640,9 @@
     pid_t pid = 123'456'789 + i;
 
     unique_fd intercept_fd, output_fd;
-    tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+    InterceptStatus status;
+    tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+    ASSERT_EQ(InterceptStatus::kRegistered, status);
 
     {
       unique_fd tombstoned_socket, input_fd;
@@ -630,7 +674,9 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       // Pretend to crash, and then immediately close the socket.
       unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -661,7 +707,9 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
+      InterceptStatus status;
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, status);
 
       {
         unique_fd tombstoned_socket, input_fd;
@@ -685,3 +733,65 @@
     thread.join();
   }
 }
+
+TEST(tombstoned, java_trace_intercept_smoke) {
+  // Using a "real" PID is a little dangerous here - if the test fails
+  // or crashes, we might end up getting a bogus / unreliable stack
+  // trace.
+  const pid_t self = getpid();
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  // First connect to tombstoned requesting a native backtrace. This
+  // should result in a "regular" FD and not the installed intercept.
+  const char native[] = "native";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  // Then, connect to tombstoned asking for a java backtrace. This *should*
+  // trigger the intercept.
+  const char java[] = "java";
+  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(java)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("java", outbuf);
+}
+
+TEST(tombstoned, multiple_intercepts) {
+  const pid_t fake_pid = 1'234'567;
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  unique_fd intercept_fd_2, output_fd_2;
+  tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+}
+
+TEST(tombstoned, intercept_any) {
+  const pid_t fake_pid = 1'234'567;
+
+  unique_fd intercept_fd, output_fd;
+  InterceptStatus status;
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, status);
+
+  const char any[] = "any";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(any)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("any", outbuf);
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 43104ec..06d4a9b 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -45,8 +45,8 @@
 #include "tombstoned/tombstoned.h"
 #include "util.h"
 
-#include "backtrace.h"
-#include "tombstone.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
 
 using android::base::unique_fd;
 
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 1275229..d41dc67 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -48,10 +48,13 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 
 #include "dump_type.h"
 
+using android::base::unique_fd;
+
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
 
@@ -117,13 +120,12 @@
 }
 
 static bool get_main_thread_name(char* buf, size_t len) {
-  int fd = open("/proc/self/comm", O_RDONLY | O_CLOEXEC);
+  unique_fd fd(open("/proc/self/comm", O_RDONLY | O_CLOEXEC));
   if (fd == -1) {
     return false;
   }
 
   ssize_t rc = read(fd, buf, len);
-  close(fd);
   if (rc == -1) {
     return false;
   } else if (rc == 0) {
@@ -302,8 +304,8 @@
   TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
   TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
 
-  int pipefds[2];
-  if (pipe(pipefds) != 0) {
+  unique_fd pipe_read, pipe_write;
+  if (!android::base::Pipe(&pipe_read, &pipe_write)) {
     fatal_errno("failed to create pipe");
   }
 
@@ -313,9 +315,9 @@
     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(pipefds[1], STDOUT_FILENO));
-    close(pipefds[0]);
-    close(pipefds[1]);
+    TEMP_FAILURE_RETRY(dup2(pipe_write.get(), STDOUT_FILENO));
+    pipe_write.reset();
+    pipe_read.reset();
 
     raise_caps();
 
@@ -333,9 +335,9 @@
 
     fatal_errno("exec failed");
   } else {
-    close(pipefds[1]);
+    pipe_write.reset();
     char buf[4];
-    ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
+    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));
@@ -351,7 +353,7 @@
         thread_info->crash_dump_started = true;
       }
     }
-    close(pipefds[0]);
+    pipe_read.reset();
 
     // Don't leave a zombie child.
     int status;
diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
index ac833ae..bfb5ea4 100644
--- a/debuggerd/libdebuggerd/arm/machine.cpp
+++ b/debuggerd/libdebuggerd/arm/machine.cpp
@@ -17,6 +17,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/machine.h"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
@@ -25,8 +27,7 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs regs;
diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
index fa73c99a..ad1c951 100644
--- a/debuggerd/libdebuggerd/arm64/machine.cpp
+++ b/debuggerd/libdebuggerd/arm64/machine.cpp
@@ -17,6 +17,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/machine.h"
+
 #include <elf.h>
 #include <errno.h>
 #include <stdint.h>
@@ -27,8 +29,7 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct user_pt_regs regs;
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index 334d97f..f616e1b 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/backtrace.h"
+
 #include <errno.h>
 #include <dirent.h>
 #include <limits.h>
@@ -34,9 +36,7 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "backtrace.h"
-
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
   time_t t = time(NULL);
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
index 4e798e2..a35102f 100644
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/elf_utils.h"
+
 #include <elf.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -27,8 +29,6 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "elf_utils.h"
-
 #define NOTE_ALIGN(size)  (((size) + 3) & ~3)
 
 template <typename HdrType, typename PhdrType, typename NhdrType>
diff --git a/debuggerd/libdebuggerd/include/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/backtrace.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
diff --git a/debuggerd/libdebuggerd/include/elf_utils.h b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/elf_utils.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
diff --git a/debuggerd/libdebuggerd/include/machine.h b/debuggerd/libdebuggerd/include/libdebuggerd/machine.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/machine.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/machine.h
diff --git a/debuggerd/libdebuggerd/include/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/open_files_list.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
similarity index 82%
rename from debuggerd/libdebuggerd/include/tombstone.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 79743b6..45740df 100644
--- a/debuggerd/libdebuggerd/include/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -35,10 +35,10 @@
 int open_tombstone(std::string* path);
 
 /* Creates a tombstone file and writes the crash dump to it. */
-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);
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+                       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);
 
 void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext);
diff --git a/debuggerd/libdebuggerd/include/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
similarity index 81%
rename from debuggerd/libdebuggerd/include/utility.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index e5e5106..f481b78 100644
--- a/debuggerd/libdebuggerd/include/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -24,26 +24,9 @@
 
 #include <string>
 
+#include <android-base/macros.h>
 #include <backtrace/Backtrace.h>
 
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
-
 struct log_t{
     // Tombstone file descriptor.
     int tfd;
diff --git a/debuggerd/libdebuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
index cbf272a..1fc690b 100644
--- a/debuggerd/libdebuggerd/mips/machine.cpp
+++ b/debuggerd/libdebuggerd/mips/machine.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/machine.h"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -25,8 +27,7 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 #define R(x) (static_cast<uintptr_t>(x))
 
diff --git a/debuggerd/libdebuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
index 0a8d532..955e507 100644
--- a/debuggerd/libdebuggerd/mips64/machine.cpp
+++ b/debuggerd/libdebuggerd/mips64/machine.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/machine.h"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -25,8 +27,7 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 #define R(x) (static_cast<uintptr_t>(x))
 
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index 5c7ea70..e199db8 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/open_files_list.h"
+
 #include <dirent.h>
 #include <errno.h>
 #include <stdio.h>
@@ -31,9 +33,7 @@
 #include <android-base/file.h>
 #include <log/log.h>
 
-#include "open_files_list.h"
-
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 void populate_open_files_list(pid_t pid, OpenFilesList* list) {
   std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 49f3690..0fad2cf 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -22,9 +22,10 @@
 #include <gtest/gtest.h>
 #include <android-base/file.h>
 
+#include "libdebuggerd/utility.h"
+
 #include "BacktraceMock.h"
 #include "log_fake.h"
-#include "utility.h"
 
 const char g_expected_full_dump[] =
 "\nmemory near r1:\n"
diff --git a/debuggerd/libdebuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
index bb52b59..f8cbca7 100644
--- a/debuggerd/libdebuggerd/test/elf_fake.cpp
+++ b/debuggerd/libdebuggerd/test/elf_fake.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "elf_fake.h"
+
 #include <stdint.h>
 
 #include <string>
diff --git a/debuggerd/libdebuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
index 3336bcb..68f4013 100644
--- a/debuggerd/libdebuggerd/test/log_fake.cpp
+++ b/debuggerd/libdebuggerd/test/log_fake.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "log_fake.h"
+
 #include <errno.h>
 #include <stdarg.h>
 
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index 85e0695..acac72c 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -24,7 +24,7 @@
 
 #include "android-base/test_utils.h"
 
-#include "open_files_list.h"
+#include "libdebuggerd/open_files_list.h"
 
 // Check that we can produce a list of open files for the current process, and
 // that it includes a known open file.
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
index f40cbd4..0d4080e 100644
--- a/debuggerd/libdebuggerd/test/ptrace_fake.cpp
+++ b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "ptrace_fake.h"
+
 #include <errno.h>
 #include <signal.h>
 #include <stdarg.h>
@@ -21,8 +23,6 @@
 
 #include <string>
 
-#include "ptrace_fake.h"
-
 siginfo_t g_fake_si = {.si_signo = 0};
 
 void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 325210d..934ceba 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -22,7 +22,7 @@
 #include <gtest/gtest.h>
 #include <android-base/file.h>
 
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 #include "BacktraceMock.h"
 #include "elf_fake.h"
@@ -113,7 +113,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff ---         0     12000\n";
 #else
@@ -148,7 +148,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff r--         0     12000  /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
 #else
@@ -187,7 +187,7 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (2 entries):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff -w-         0     12000\n"
 "    12345678'9abcd000-12345678'9abdefff -w-         0     12000  /system/lib/libfake.so\n";
@@ -220,21 +220,21 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
@@ -244,20 +244,20 @@
   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:\n"
+  const char* expected_dump =
+      "\nmemory map (5 entries):\n"
 #if defined(__LP64__)
-"    00000000'0a234000-00000000'0a234fff ---         0      1000\n"
-"    00000000'0a334000-00000000'0a334fff r--      f000      1000\n"
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    00000000'0a234000-00000000'0a234fff ---         0      1000\n"
+      "    00000000'0a334000-00000000'0a334fff r--      f000      1000\n"
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"    0a234000-0a234fff ---         0      1000\n"
-"    0a334000-0a334fff r--      f000      1000\n"
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    0a234000-0a234fff ---         0      1000\n"
+      "    0a334000-0a334fff r--      f000      1000\n"
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -274,21 +274,21 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
@@ -304,18 +304,18 @@
   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: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries):\n"
 #if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "--->Fault address falls at 00001000 before any mapped regions\n"
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -332,21 +332,21 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
@@ -362,18 +362,18 @@
   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: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->Fault address falls at 0a533000 between mapped regions\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->Fault address falls at 0a533000 between mapped regions\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -390,21 +390,21 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
@@ -420,16 +420,16 @@
   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: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -446,21 +446,21 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
@@ -480,18 +480,18 @@
   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: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n"
-"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n"
+      "--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
 #else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n"
-"--->Fault address falls at 0f534040 after any mapped regions\n";
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n"
+      "--->Fault address falls at 0f534040 after any mapped regions\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -508,7 +508,7 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
@@ -520,12 +520,12 @@
   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:\n"
+  const char* expected_dump =
+      "\nmemory map (1 entry):\n"
 #if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n";
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n";
 #else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n";
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -571,7 +571,7 @@
     }
 
     const char* expected_addr_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+"\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";
@@ -580,7 +580,7 @@
 "    0a434000-0a434fff -w-         0      1000\n";
 #endif
     const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
 #else
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index edc7be5..6fb29a9 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/tombstone.h"
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -33,32 +35,30 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <android/log.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
-#include <cutils/properties.h>
 #include <log/log.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 
+// Needed to get DEBUGGER_SIGNAL.
 #include "debuggerd/handler.h"
 
-#include "backtrace.h"
-#include "elf_utils.h"
-#include "machine.h"
-#include "open_files_list.h"
-#include "tombstone.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/elf_utils.h"
+#include "libdebuggerd/machine.h"
+#include "libdebuggerd/open_files_list.h"
 
+using android::base::GetBoolProperty;
+using android::base::GetProperty;
 using android::base::StringPrintf;
 
 #define STACK_WORDS 16
 
-#define MAX_TOMBSTONES  10
-#define TOMBSTONE_DIR   "/data/tombstones"
-#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
-
 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) {
@@ -168,6 +168,26 @@
         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;
   }
@@ -188,14 +208,11 @@
 }
 
 static void dump_header_info(log_t* log) {
-  char fingerprint[PROPERTY_VALUE_MAX];
-  char revision[PROPERTY_VALUE_MAX];
+  auto fingerprint = GetProperty("ro.build.fingerprint", "unknown");
+  auto revision = GetProperty("ro.revision", "unknown");
 
-  property_get("ro.build.fingerprint", fingerprint, "unknown");
-  property_get("ro.revision", revision, "unknown");
-
-  _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint);
-  _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision);
+  _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint.c_str());
+  _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision.c_str());
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
@@ -395,16 +412,20 @@
   }
 
   ScopedBacktraceMapIteratorLock lock(map);
-  _LOG(log, logtype::MAPS, "\n");
-  if (!print_fault_address_marker) {
-    _LOG(log, logtype::MAPS, "memory map:\n");
-  } else {
-    _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
+  _LOG(log, logtype::MAPS,
+       "\n"
+       "memory map (%zu entr%s):",
+       map->size(), map->size() == 1 ? "y" : "ies");
+  if (print_fault_address_marker) {
     if (map->begin() != map->end() && addr < map->begin()->start) {
-      _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
+      _LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
            get_addr_string(addr).c_str());
       print_fault_address_marker = false;
+    } else {
+      _LOG(log, logtype::MAPS, " (fault address prefixed with --->)\n");
     }
+  } else {
+    _LOG(log, logtype::MAPS, "\n");
   }
 
   std::string line;
@@ -446,11 +467,11 @@
         line += " (BuildId: " + build_id + ")";
       }
     }
-    if (it->load_base != 0) {
+    if (it->load_bias != 0) {
       if (space_needed) {
         line += ' ';
       }
-      line += StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+      line += StringPrintf(" (load bias 0x%" PRIxPTR ")", it->load_bias);
     }
     _LOG(log, logtype::MAPS, "%s\n", line.c_str());
   }
@@ -475,8 +496,55 @@
   _LOG(log, logtype::REGISTERS, "    register dumping unimplemented on this architecture");
 }
 
-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,
+static bool verify_backtraces_equal(Backtrace* back1, Backtrace* back2) {
+  if (back1->NumFrames() != back2->NumFrames()) {
+    return false;
+  }
+  std::string back1_str;
+  std::string back2_str;
+  for (size_t i = 0; i < back1->NumFrames(); i++) {
+    back1_str += back1->FormatFrameData(i);
+    back2_str += back2->FormatFrameData(i);
+  }
+  return back1_str == back2_str;
+}
+
+static void log_mismatch_data(log_t* log, Backtrace* backtrace) {
+  _LOG(log, logtype::THREAD, "MISMATCH: This unwind is different.\n");
+  if (backtrace->NumFrames() == 0) {
+    _LOG(log, logtype::THREAD, "MISMATCH: No frames in new backtrace.\n");
+    return;
+  }
+  _LOG(log, logtype::THREAD, "MISMATCH: Backtrace from new unwinder.\n");
+  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+    _LOG(log, logtype::THREAD, "MISMATCH: %s\n", backtrace->FormatFrameData(i).c_str());
+  }
+
+  // Get the stack trace up to 8192 bytes.
+  std::vector<uint64_t> buffer(8192 / sizeof(uint64_t));
+  size_t bytes =
+      backtrace->Read(backtrace->GetFrame(0)->sp, reinterpret_cast<uint8_t*>(buffer.data()),
+                      buffer.size() * sizeof(uint64_t));
+  std::string log_data;
+  for (size_t i = 0; i < bytes / sizeof(uint64_t); i++) {
+    if ((i % 4) == 0) {
+      if (!log_data.empty()) {
+        _LOG(log, logtype::THREAD, "MISMATCH: stack_data%s\n", log_data.c_str());
+        log_data = "";
+      }
+    }
+    log_data += android::base::StringPrintf(" 0x%016" PRIx64, buffer[i]);
+  }
+
+  if (!log_data.empty()) {
+    _LOG(log, logtype::THREAD, "MISMATCH: data%s\n", log_data.c_str());
+  }
+
+  // If there is any leftover (bytes % sizeof(uint64_t) != 0, ignore it for now.
+}
+
+static bool dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
+                        const std::string& thread_name, BacktraceMap* map, BacktraceMap* map_new,
                         uintptr_t abort_msg_address, bool primary_thread) {
   log->current_tid = tid;
   if (!primary_thread) {
@@ -490,7 +558,18 @@
     dump_abort_message(backtrace.get(), log, abort_msg_address);
   }
   dump_registers(log, tid);
+  bool matches = true;
   if (backtrace->Unwind(0)) {
+    // Use the new method and verify it is the same as old.
+    std::unique_ptr<Backtrace> backtrace_new(Backtrace::CreateNew(pid, tid, map_new));
+    if (!backtrace_new->Unwind(0)) {
+      _LOG(log, logtype::THREAD, "Failed to unwind with new unwinder: %s\n",
+           backtrace_new->GetErrorString(backtrace_new->GetError()).c_str());
+      matches = false;
+    } else if (!verify_backtraces_equal(backtrace.get(), backtrace_new.get())) {
+      log_mismatch_data(log, backtrace_new.get());
+      matches = false;
+    }
     dump_backtrace_and_stack(backtrace.get(), log);
   } else {
     ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
@@ -504,6 +583,8 @@
   }
 
   log->current_tid = log->crashed_tid;
+
+  return matches;
 }
 
 // Reads the contents of the specified log device, filters out the entries
@@ -637,18 +718,18 @@
 }
 
 // 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) {
+static void dump_crash(log_t* log, BacktraceMap* map, BacktraceMap* map_new,
+                       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
-  char value[PROPERTY_VALUE_MAX];
-  property_get("ro.debuggable", value, "0");
-  bool want_logs = (value[0] == '1');
+  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);
+  bool new_unwind_matches = dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map,
+                                        map_new, abort_msg_address, true);
   if (want_logs) {
     dump_logs(log, pid, 5);
   }
@@ -658,7 +739,9 @@
     const std::string& thread_name = it.second;
 
     if (thread_tid != tid) {
-      dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
+      bool match =
+          dump_thread(log, pid, thread_tid, process_name, thread_name, map, map_new, 0, false);
+      new_unwind_matches = new_unwind_matches && match;
     }
   }
 
@@ -670,71 +753,26 @@
   if (want_logs) {
     dump_logs(log, pid, 0);
   }
+  if (!new_unwind_matches) {
+    _LOG(log, logtype::THREAD, "MISMATCH: New and old unwinder do not agree.\n");
+    _LOG(log, logtype::THREAD, "MISMATCH: If you see this please file a bug in:\n");
+    _LOG(log, logtype::THREAD,
+         "MISMATCH: Android > Android OS & Apps > Runtime > native > tools "
+         "(debuggerd/gdb/init/simpleperf/strace/valgrind)\n");
+    _LOG(log, logtype::THREAD, "MISMATCH: and attach this tombstone.\n");
+  }
 }
 
-// open_tombstone - find an available tombstone slot, if any, of the
-// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
-// file is available, we reuse the least-recently-modified file.
-int open_tombstone(std::string* out_path) {
-  // In a single pass, find an available slot and, in case none
-  // exist, find and record the least-recently-modified file.
-  char path[128];
-  int fd = -1;
-  int oldest = -1;
-  struct stat oldest_sb;
-  for (int i = 0; i < MAX_TOMBSTONES; i++) {
-    snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
-
-    struct stat sb;
-    if (stat(path, &sb) == 0) {
-      if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
-        oldest = i;
-        oldest_sb.st_mtime = sb.st_mtime;
-      }
-      continue;
-    }
-    if (errno != ENOENT) continue;
-
-    fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
-    if (fd < 0) continue;  // raced ?
-
-    if (out_path) {
-      *out_path = path;
-    }
-    fchown(fd, AID_SYSTEM, AID_SYSTEM);
-    return fd;
-  }
-
-  if (oldest < 0) {
-    ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
-    oldest = 0;
-  }
-
-  // we didn't find an available file, so we clobber the oldest one
-  snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
-  fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
-  if (fd < 0) {
-    ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
-    return -1;
-  }
-
-  if (out_path) {
-    *out_path = path;
-  }
-  fchown(fd, AID_SYSTEM, AID_SYSTEM);
-  return fd;
-}
-
-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) {
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+                       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);
+  dump_crash(&log, map, map_new, 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,
@@ -763,10 +801,22 @@
   dump_abort_message(backtrace.get(), &log, abort_msg_address);
   dump_registers(&log, ucontext);
 
-  // TODO: Dump registers from the ucontext.
   if (backtrace->Unwind(0, ucontext)) {
     dump_backtrace_and_stack(backtrace.get(), &log);
   } else {
     ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
   }
+
+  // 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);
+    }
+  }
 }
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 22fde5e..1b74652 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -16,22 +16,28 @@
 
 #define LOG_TAG "DEBUG"
 
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
 #include <sys/ptrace.h>
+#include <sys/uio.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 #include <string>
 
+#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 <backtrace/Backtrace.h>
 #include <log/log.h>
 
+using android::base::unique_fd;
+
 // Whitelist output desired in the logcat output.
 bool is_allowed_in_logcat(enum logtype ltype) {
   if ((ltype == HEADER)
@@ -42,6 +48,19 @@
   return false;
 }
 
+static bool should_write_to_kmsg() {
+  // Write to kmsg if tombstoned isn't up, and we're able to do so.
+  if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+    return false;
+  }
+
+  if (android::base::GetProperty("init.svc.tombstoned", "") == "running") {
+    return false;
+  }
+
+  return true;
+}
+
 __attribute__((__weak__, visibility("default")))
 void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
   bool write_to_tombstone = (log->tfd != -1);
@@ -49,6 +68,7 @@
                       && log->crashed_tid != -1
                       && log->current_tid != -1
                       && (log->crashed_tid == log->current_tid);
+  static bool write_to_kmsg = should_write_to_kmsg();
 
   char buf[512];
   va_list ap;
@@ -70,6 +90,30 @@
     if (log->amfd_data != nullptr) {
       *log->amfd_data += buf;
     }
+
+    if (write_to_kmsg) {
+      unique_fd kmsg_fd(open("/dev/kmsg_debug", O_WRONLY | O_APPEND | O_CLOEXEC));
+      if (kmsg_fd.get() >= 0) {
+        // Our output might contain newlines which would otherwise be handled by the android logger.
+        // Split the lines up ourselves before sending to the kernel logger.
+        if (buf[len - 1] == '\n') {
+          buf[len - 1] = '\0';
+        }
+
+        std::vector<std::string> fragments = android::base::Split(buf, "\n");
+        for (const std::string& fragment : fragments) {
+          static constexpr char prefix[] = "<3>DEBUG: ";
+          struct iovec iov[3];
+          iov[0].iov_base = const_cast<char*>(prefix);
+          iov[0].iov_len = strlen(prefix);
+          iov[1].iov_base = const_cast<char*>(fragment.c_str());
+          iov[1].iov_len = fragment.length();
+          iov[2].iov_base = const_cast<char*>("\n");
+          iov[2].iov_len = 1;
+          TEMP_FAILURE_RETRY(writev(kmsg_fd.get(), iov, 3));
+        }
+      }
+    }
   }
 }
 
@@ -205,7 +249,7 @@
 }
 
 void read_with_default(const char* path, char* buf, size_t len, const char* default_value) {
-  android::base::unique_fd fd(open(path, O_RDONLY));
+  unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
   if (fd != -1) {
     int rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, len - 1));
     if (rc != -1) {
diff --git a/debuggerd/libdebuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
index af10817..09a64cd 100644
--- a/debuggerd/libdebuggerd/x86/machine.cpp
+++ b/debuggerd/libdebuggerd/x86/machine.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/machine.h"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
@@ -24,8 +26,7 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct pt_regs r;
diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
index bf2c2b4..de1c268 100644
--- a/debuggerd/libdebuggerd/x86_64/machine.cpp
+++ b/debuggerd/libdebuggerd/x86_64/machine.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/machine.h"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
@@ -25,8 +27,7 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct user_regs_struct r;
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 24960bc..c446dbb 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -185,8 +185,8 @@
 }
 
 InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
-  this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
-                                      intercept_socket);
+  this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,
+                                      /* backlog */ -1, intercept_socket);
 }
 
 bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 09cff45..1bf8f14 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -103,7 +103,7 @@
   }
 
   static CrashQueue* for_anrs() {
-    static CrashQueue queue("/data/anr", "anr_" /* file_name_prefix */,
+    static CrashQueue queue("/data/anr", "trace_" /* file_name_prefix */,
                             GetIntProperty("tombstoned.max_anr_count", 64),
                             4 /* max_concurrent_dumps */);
     return &queue;
@@ -194,7 +194,7 @@
 };
 
 // Whether java trace dumps are produced via tombstoned.
-static constexpr bool kJavaTraceDumpsEnabled = false;
+static constexpr bool kJavaTraceDumpsEnabled = true;
 
 // Forward declare the callbacks so they can be placed in a sensible order.
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
@@ -343,7 +343,14 @@
   }
 
   if (!crash->crash_path.empty()) {
-    LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+    if (crash->crash_type == kDebuggerdJavaBacktrace) {
+      LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << crash->crash_path;
+    } else {
+      // NOTE: Several tools parse this log message to figure out where the
+      // tombstone associated with a given native crash was written. Any changes
+      // to this message must be carefully considered.
+      LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+    }
   }
 
 fail:
@@ -382,8 +389,9 @@
 
   intercept_manager = new InterceptManager(base, intercept_socket);
 
-  evconnlistener* tombstone_listener = evconnlistener_new(
-      base, crash_accept_cb, CrashQueue::for_tombstones(), -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+  evconnlistener* tombstone_listener =
+      evconnlistener_new(base, crash_accept_cb, CrashQueue::for_tombstones(), LEV_OPT_CLOSE_ON_FREE,
+                         -1 /* backlog */, crash_socket);
   if (!tombstone_listener) {
     LOG(FATAL) << "failed to create evconnlistener for tombstones.";
   }
@@ -395,8 +403,9 @@
     }
 
     evutil_make_socket_nonblocking(java_trace_socket);
-    evconnlistener* java_trace_listener = evconnlistener_new(
-        base, crash_accept_cb, CrashQueue::for_anrs(), -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+    evconnlistener* java_trace_listener =
+        evconnlistener_new(base, crash_accept_cb, CrashQueue::for_anrs(), LEV_OPT_CLOSE_ON_FREE,
+                           -1 /* backlog */, java_trace_socket);
     if (!java_trace_listener) {
       LOG(FATAL) << "failed to create evconnlistener for java traces.";
     }
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index b8345ca..53ef01c 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -7,4 +7,5 @@
 
     socket tombstoned_crash seqpacket 0666 system system
     socket tombstoned_intercept seqpacket 0666 system system
+    socket tombstoned_java_trace seqpacket 0666 system system
     writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index c6a997b..0bb07ac 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -86,13 +86,3 @@
 
   return result;
 }
-
-bool Pipe(unique_fd* read, unique_fd* write) {
-  int pipefds[2];
-  if (pipe(pipefds) != 0) {
-    return false;
-  }
-  read->reset(pipefds[0]);
-  write->reset(pipefds[1]);
-  return true;
-}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 6051714..171e07a 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -42,5 +42,3 @@
 //   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);
-
-bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write);
diff --git a/demangle/Android.bp b/demangle/Android.bp
index ce617a7..89b8772 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -24,14 +24,18 @@
         "-Werror",
         "-Wextra",
     ],
+
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+    },
 }
 
 cc_library {
     name: "libdemangle",
-
-    vendor_available: true,
-
     defaults: ["libdemangle_defaults"],
+    vendor_available: true,
 
     srcs: [
         "Demangler.cpp",
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index f56a9be..c93e2ab 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -428,6 +428,14 @@
   ASSERT_EQ("_ZN3one3twoEDa", demangler.Parse("_ZN3one3twoEDa", 12));
 }
 
+TEST(DemangleTest, BooleanLiterals) {
+  Demangler demangler;
+
+  ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
+  ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
+  ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
+}
+
 TEST(DemangleTest, demangle) {
   std::string str;
 
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index c0a96aa..f148b21 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -660,6 +660,29 @@
   return nullptr;
 }
 
+const char* Demangler::ParseTemplateLiteral(const char* name) {
+  if (*name == 'E') {
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+    return name + 1;
+  }
+  // Only understand boolean values with 0 or 1.
+  if (*name == 'b') {
+    name++;
+    if (*name == '0') {
+      AppendArgument("false");
+      cur_state_.str.clear();
+    } else if (*name == '1') {
+      AppendArgument("true");
+      cur_state_.str.clear();
+    } else {
+      return nullptr;
+    }
+    return name + 1;
+  }
+  return nullptr;
+}
+
 const char* Demangler::ParseTemplateArgumentsComplex(const char* name) {
   if (*name == 'E') {
     if (parse_funcs_.empty()) {
@@ -670,6 +693,11 @@
     FinalizeTemplate();
     Save(cur_state_.str, false);
     return name + 1;
+  } else if (*name == 'L') {
+    // Literal value for a template.
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateLiteral;
+    return name + 1;
   }
   return ParseArguments(name);
 }
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
index 3bd4f3c..f76def6 100644
--- a/demangle/Demangler.h
+++ b/demangle/Demangler.h
@@ -92,6 +92,7 @@
   const char* ParseArguments(const char* name);
   const char* ParseTemplateArguments(const char* name);
   const char* ParseTemplateArgumentsComplex(const char* name);
+  const char* ParseTemplateLiteral(const char* name);
   const char* ParseFunctionArgument(const char* name);
   const char* ParseFunctionName(const char* name);
   const char* FindFunctionName(const char* name);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 271ca95..5f2267c 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -93,6 +93,9 @@
 static unsigned second_offset  = 0x00f00000;
 static unsigned tags_offset    = 0x00000100;
 
+static bool g_disable_verity = false;
+static bool g_disable_verification = false;
+
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
 enum fb_buffer_type {
@@ -419,6 +422,10 @@
             "  --skip-reboot                            Will not reboot the device when\n"
             "                                           performing commands that normally\n"
             "                                           trigger a reboot.\n"
+            "  --disable-verity                         Set the disable-verity flag in the\n"
+            "                                           the vbmeta image being flashed.\n"
+            "  --disable-verification                   Set the disable-verification flag in"
+            "                                           the vbmeta image being flashed.\n"
 #if !defined(_WIN32)
             "  --wipe-and-use-fbe                       On devices which support it,\n"
             "                                           erase userdata and cache, and\n"
@@ -858,10 +865,55 @@
     return load_buf_fd(transport, fd.release(), buf);
 }
 
+static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
+    // Buffer needs to be at least the size of the VBMeta struct which
+    // is 256 bytes.
+    if (buf->sz < 256) {
+        return;
+    }
+
+    int fd = make_temporary_fd();
+    if (fd == -1) {
+        die("Failed to create temporary file for vbmeta rewriting");
+    }
+
+    std::string data;
+    if (!android::base::ReadFdToString(buf->fd, &data)) {
+        die("Failed reading from vbmeta");
+    }
+
+    // There's a 32-bit big endian |flags| field at offset 120 where
+    // bit 0 corresponds to disable-verity and bit 1 corresponds to
+    // disable-verification.
+    //
+    // See external/avb/libavb/avb_vbmeta_image.h for the layout of
+    // the VBMeta struct.
+    if (g_disable_verity) {
+        data[123] |= 0x01;
+    }
+    if (g_disable_verification) {
+        data[123] |= 0x02;
+    }
+
+    if (!android::base::WriteStringToFd(data, fd)) {
+        die("Failed writing to modified vbmeta");
+    }
+    close(buf->fd);
+    buf->fd = fd;
+    lseek(fd, 0, SEEK_SET);
+}
+
 static void flash_buf(const char *pname, struct fastboot_buffer *buf)
 {
     sparse_file** s;
 
+    // Rewrite vbmeta if that's what we're flashing and modification has been requested.
+    if ((g_disable_verity || g_disable_verification) &&
+        (strcmp(pname, "vbmeta") == 0 || strcmp(pname, "vbmeta_a") == 0 ||
+         strcmp(pname, "vbmeta_b") == 0)) {
+        rewrite_vbmeta_buffer(buf);
+    }
+
     switch (buf->type) {
         case FB_BUFFER_SPARSE: {
             std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
@@ -1411,7 +1463,7 @@
 
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
-        fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
+        die("Cannot generate image for %s\n", partition);
         return;
     }
 
@@ -1470,6 +1522,8 @@
         {"set-active", optional_argument, 0, 'a'},
         {"skip-secondary", no_argument, 0, 0},
         {"skip-reboot", no_argument, 0, 0},
+        {"disable-verity", no_argument, 0, 0},
+        {"disable-verification", no_argument, 0, 0},
 #if !defined(_WIN32)
         {"wipe-and-use-fbe", no_argument, 0, 0},
 #endif
@@ -1555,6 +1609,10 @@
                 skip_secondary = true;
             } else if (strcmp("skip-reboot", longopts[longindex].name) == 0 ) {
                 skip_reboot = true;
+            } else if (strcmp("disable-verity", longopts[longindex].name) == 0 ) {
+                g_disable_verity = true;
+            } else if (strcmp("disable-verification", longopts[longindex].name) == 0 ) {
+                g_disable_verification = true;
 #if !defined(_WIN32)
             } else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) {
                 wants_wipe = true;
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 4a4a7c0..709f061 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -124,6 +124,8 @@
     }
     mke2fs_args.push_back("-E");
     mke2fs_args.push_back(ext_attr.c_str());
+    mke2fs_args.push_back("-O");
+    mke2fs_args.push_back("uninit_bg");
     mke2fs_args.push_back(fileName);
 
     std::string size_str = std::to_string(partSize / block_size);
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 9069baa..e95b049 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -93,12 +93,9 @@
     SInt32 score;
     UInt8 interfaceNumEndpoints;
 
-    // Placing the constant KIOUSBFindInterfaceDontCare into the following
-    // fields of the IOUSBFindInterfaceRequest structure will allow us to
-    // find all of the interfaces
-    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
-    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
-    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceClass = 0xff;
+    request.bInterfaceSubClass = 0x42;
+    request.bInterfaceProtocol = 0x03;
     request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
 
     // Get an iterator for the interfaces on the device
@@ -282,7 +279,6 @@
             &plugin, &score);
 
     if ((kr != 0) || (plugin == NULL)) {
-        ERR("Unable to create a plug-in (%08x)\n", kr);
         goto error;
     }
 
@@ -436,8 +432,7 @@
 
         if (try_device(device, &h) != 0) {
             IOObjectRelease(device);
-            ret = -1;
-            break;
+            continue;
         }
 
         if (h.success) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 4441ad0..5a6298e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -21,20 +21,6 @@
     },
     local_include_dirs: ["include/"],
     cppflags: ["-Werror"],
-    static_libs: [
-        "libfec",
-        "libfec_rs",
-        "libbase",
-        "libcrypto_utils",
-        "libcrypto",
-        "libext4_utils",
-        "libsquashfs_utils",
-        "libselinux",
-        "libavb",
-    ],
-    whole_static_libs: [
-        "liblogwrap",
-    ],
 }
 
 cc_library_static {
@@ -46,19 +32,51 @@
         "fs_mgr.cpp",
         "fs_mgr_dm_ioctl.cpp",
         "fs_mgr_format.cpp",
-        "fs_mgr_fstab.cpp",
-        "fs_mgr_slotselect.cpp",
         "fs_mgr_verity.cpp",
         "fs_mgr_avb.cpp",
         "fs_mgr_avb_ops.cpp",
-        "fs_mgr_boot_config.cpp",
+    ],
+    static_libs: [
+        "libfec",
+        "libfec_rs",
+        "libbase",
+        "libcrypto_utils",
+        "libcrypto",
+        "libext4_utils",
+        "libsquashfs_utils",
+        "libselinux",
+        "libavb",
+        "libfstab",
+    ],
+    export_static_lib_headers: [
+        "libfstab",
+    ],
+    whole_static_libs: [
+        "liblogwrap",
+        "libfstab",
+    ],
+    cppflags: [
+        "-DALLOW_ADBD_DISABLE_VERITY=0",
     ],
     product_variables: {
         debuggable: {
-            cppflags: ["-DALLOW_ADBD_DISABLE_VERITY=1"],
-        },
-        eng: {
-            cppflags: ["-DALLOW_SKIP_SECURE_CHECK=1"],
+            cppflags: [
+                "-UALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_DISABLE_VERITY=1",
+            ],
         },
     },
 }
+
+cc_library_static {
+    name: "libfstab",
+    vendor_available: true,
+    defaults: ["fs_mgr_defaults"],
+    srcs: [
+        "fs_mgr_fstab.cpp",
+        "fs_mgr_boot_config.cpp",
+        "fs_mgr_slotselect.cpp",
+    ],
+    export_include_dirs: ["include_fstab"],
+    header_libs: ["libbase_headers"],
+}
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 9249343..007189d 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -15,7 +15,6 @@
     libavb
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
 LOCAL_SRC_FILES:= fs_mgr_main.cpp
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e009383..4b94f9c 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -32,6 +32,7 @@
 #include <unistd.h>
 
 #include <memory>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -78,43 +79,33 @@
     FS_STAT_E2FSCK_F_ALWAYS = 0x0004,
     FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,
     FS_STAT_QUOTA_ENABLED = 0x0010,
-    FS_STAT_TUNE2FS_FAILED = 0x0020,
     FS_STAT_RO_MOUNT_FAILED = 0x0040,
     FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
     FS_STAT_FULL_MOUNT_FAILED = 0x0100,
     FS_STAT_E2FSCK_FAILED = 0x0200,
     FS_STAT_E2FSCK_FS_FIXED = 0x0400,
     FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
+    FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
+    FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
+    FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
 };
 
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-static time_t gettime(void)
-{
-    struct timespec ts;
-    int ret;
+// TODO: switch to inotify()
+bool fs_mgr_wait_for_file(const std::string& filename,
+                          const std::chrono::milliseconds relative_timeout) {
+    auto start_time = std::chrono::steady_clock::now();
 
-    ret = clock_gettime(CLOCK_MONOTONIC, &ts);
-    if (ret < 0) {
-        PERROR << "clock_gettime(CLOCK_MONOTONIC) failed";
-        return 0;
+    while (true) {
+        if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
+            return true;
+        }
+
+        std::this_thread::sleep_for(50ms);
+
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (time_elapsed > relative_timeout) return false;
     }
-
-    return ts.tv_sec;
-}
-
-static int wait_for_file(const char *filename, int timeout)
-{
-    struct stat info;
-    time_t timeout_time = gettime() + timeout;
-    int ret = -1;
-
-    while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
-        usleep(10000);
-
-    return ret;
 }
 
 static void log_fs_stat(const char* blk_device, int fs_stat)
@@ -128,10 +119,16 @@
     }
 }
 
+static bool is_extfs(const std::string& fs_type) {
+    return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
+}
+
 static bool should_force_check(int fs_stat) {
-    return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
-                      FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
-                      FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+    return fs_stat &
+           (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+            FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
+            FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+            FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
 }
 
 static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
@@ -144,7 +141,7 @@
     const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
 
     /* Check for the types of filesystems we know how to check */
-    if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+    if (is_extfs(fs_type)) {
         if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {  // will fail, so do not try
             return;
         }
@@ -242,186 +239,214 @@
     return;
 }
 
-/* Function to read the primary superblock */
-static int read_super_block(int fd, struct ext4_super_block *sb)
-{
-    off64_t ret;
-
-    ret = lseek64(fd, 1024, SEEK_SET);
-    if (ret < 0)
-        return ret;
-
-    ret = read(fd, sb, sizeof(*sb));
-    if (ret < 0)
-        return ret;
-    if (ret != sizeof(*sb))
-        return ret;
-
-    return 0;
-}
-
-static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_blocks_count(const struct ext4_super_block* es) {
     return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |
-            le32_to_cpu(es->s_blocks_count_lo);
+           le32_to_cpu(es->s_blocks_count_lo);
 }
 
-static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_r_blocks_count(const struct ext4_super_block* es) {
     return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |
-            le32_to_cpu(es->s_r_blocks_count_lo);
+           le32_to_cpu(es->s_r_blocks_count_lo);
 }
 
-static int do_quota_with_shutdown_check(char *blk_device, char *fs_type,
-                                        struct fstab_rec *rec, int *fs_stat)
-{
-    int force_check = 0;
-    if (!strcmp(fs_type, "ext4")) {
-        /*
-         * Some system images do not have tune2fs for licensing reasons
-         * Detect these and skip reserve blocks.
-         */
-        if (access(TUNE2FS_BIN, X_OK)) {
-            LERROR << "Not running " << TUNE2FS_BIN << " on "
-                   << blk_device << " (executable not in system image)";
-        } else {
-            const char* arg1 = nullptr;
-            const char* arg2 = nullptr;
-            int status = 0;
-            int ret = 0;
-            android::base::unique_fd fd(
-                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
-            if (fd >= 0) {
-                struct ext4_super_block sb;
-                ret = read_super_block(fd, &sb);
-                if (ret < 0) {
-                    PERROR << "Can't read '" << blk_device << "' super block";
-                    return force_check;
-                }
-                if (sb.s_magic != EXT4_SUPER_MAGIC) {
-                    LINFO << "Invalid ext4 magic:0x" << std::hex << sb.s_magic << "," << blk_device;
-                    *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
-                    return 0;  // not a valid fs, tune2fs, fsck, and mount  will all fail.
-                }
-                *fs_stat |= FS_STAT_IS_EXT4;
-                LINFO << "superblock s_max_mnt_count:" << sb.s_max_mnt_count << "," << blk_device;
-                if (sb.s_max_mnt_count == 0xffff) {  // -1 (int16) in ext2, but uint16 in ext4
-                    *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
-                }
-                if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
-                    (sb.s_state & EXT4_VALID_FS) == 0) {
-                    LINFO << __FUNCTION__ << "(): was not clealy shutdown, state flag:"
-                          << std::hex << sb.s_state
-                          << "incompat flag:" << std::hex << sb.s_feature_incompat;
-                    force_check = 1;
-                    *fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
-                }
-                int has_quota = (sb.s_feature_ro_compat
-                        & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
-                int want_quota = fs_mgr_is_quota(rec) != 0;
-
-                if (has_quota == want_quota) {
-                    LINFO << "Requested quota status is match on " << blk_device;
-                    return force_check;
-                } else if (want_quota) {
-                    LINFO << "Enabling quota on " << blk_device;
-                    arg1 = "-Oquota";
-                    arg2 = "-Qusrquota,grpquota";
-                    force_check = 1;
-                    *fs_stat |= FS_STAT_QUOTA_ENABLED;
-                } else {
-                    LINFO << "Disabling quota on " << blk_device;
-                    arg1 = "-Q^usrquota,^grpquota";
-                    arg2 = "-O^quota";
-                }
-            } else {
-                PERROR << "Failed to open '" << blk_device << "'";
-                return force_check;
-            }
-
-            const char *tune2fs_argv[] = {
-                TUNE2FS_BIN,
-                arg1,
-                arg2,
-                blk_device,
-            };
-            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
-                                          const_cast<char **>(tune2fs_argv),
-                                          &status, true, LOG_KLOG | LOG_FILE,
-                                          true, NULL, NULL, 0);
-            if (ret < 0) {
-                /* No need to check for error in fork, we can't really handle it now */
-                LERROR << "Failed trying to run " << TUNE2FS_BIN;
-                *fs_stat |= FS_STAT_TUNE2FS_FAILED;
-            }
-        }
-    }
-    return force_check;
+static bool is_ext4_superblock_valid(const struct ext4_super_block* es) {
+    if (es->s_magic != EXT4_SUPER_MAGIC) return false;
+    if (es->s_rev_level != EXT4_DYNAMIC_REV && es->s_rev_level != EXT4_GOOD_OLD_REV) return false;
+    if (EXT4_INODES_PER_GROUP(es) == 0) return false;
+    return true;
 }
 
-static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec, int *fs_stat)
-{
-    /* Check for the types of filesystems we know how to check */
-    if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
-        /*
-         * Some system images do not have tune2fs for licensing reasons
-         * Detect these and skip reserve blocks.
-         */
-        if (access(TUNE2FS_BIN, X_OK)) {
-            LERROR << "Not running " << TUNE2FS_BIN << " on "
-                   << blk_device << " (executable not in system image)";
+// Read the primary superblock from an ext4 filesystem.  On failure return
+// false.  If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
+static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+
+    if (fd < 0) {
+        PERROR << "Failed to open '" << blk_device << "'";
+        return false;
+    }
+
+    if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+        PERROR << "Can't read '" << blk_device << "' superblock";
+        return false;
+    }
+
+    if (!is_ext4_superblock_valid(sb)) {
+        LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
+        // not a valid fs, tune2fs, fsck, and mount  will all fail.
+        *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+        return false;
+    }
+    *fs_stat |= FS_STAT_IS_EXT4;
+    LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
+    if (sb->s_max_mnt_count == 0xffff) {  // -1 (int16) in ext2, but uint16 in ext4
+        *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
+    }
+    return true;
+}
+
+// Some system images do not have tune2fs for licensing reasons.
+// Detect these and skip running it.
+static bool tune2fs_available(void) {
+    return access(TUNE2FS_BIN, X_OK) == 0;
+}
+
+static bool run_tune2fs(const char* argv[], int argc) {
+    int ret;
+
+    ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
+                                  LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
+    return ret == 0;
+}
+
+// Enable/disable quota support on the filesystem if needed.
+static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+                       const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+    bool want_quota = fs_mgr_is_quota(rec) != 0;
+
+    if (has_quota == want_quota) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to " << (want_quota ? "enable" : "disable") << " quotas on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+
+    if (want_quota) {
+        LINFO << "Enabling quotas on " << blk_device;
+        argv[1] = "-Oquota";
+        argv[2] = "-Qusrquota,grpquota";
+        *fs_stat |= FS_STAT_QUOTA_ENABLED;
+    } else {
+        LINFO << "Disabling quotas on " << blk_device;
+        argv[1] = "-O^quota";
+        argv[2] = "-Q^usrquota,^grpquota";
+    }
+
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to " << (want_quota ? "enable" : "disable")
+               << " quotas on " << blk_device;
+        *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED;
+    }
+}
+
+// Set the number of reserved filesystem blocks if needed.
+static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+                               const struct ext4_super_block* sb, int* fs_stat) {
+    if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+        return;
+    }
+
+    // The size to reserve is given in the fstab, but we won't reserve more
+    // than 2% of the filesystem.
+    const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
+    uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+
+    if (reserved_blocks > max_reserved_blocks) {
+        LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
+                 << "capping to " << max_reserved_blocks;
+        reserved_blocks = max_reserved_blocks;
+    }
+
+    if (ext4_r_blocks_count(sb) == reserved_blocks) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to set the number of reserved blocks on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    char buf[32];
+    const char* argv[] = {TUNE2FS_BIN, "-r", buf, blk_device};
+
+    snprintf(buf, sizeof(buf), "%" PRIu64, reserved_blocks);
+    LINFO << "Setting reserved block count on " << blk_device << " to " << reserved_blocks;
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
+               << blk_device;
+        *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED;
+    }
+}
+
+// Enable file-based encryption if needed.
+static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+                         const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
+    bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+
+    if (has_encrypt || !want_encrypt) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 encryption on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+
+    LINFO << "Enabling ext4 encryption on " << blk_device;
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 encryption on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
+    }
+}
+
+//
+// Prepare the filesystem on the given block device to be mounted.
+//
+// If the "check" option was given in the fstab record, or it seems that the
+// filesystem was uncleanly shut down, we'll run fsck on the filesystem.
+//
+// If needed, we'll also enable (or disable) filesystem features as specified by
+// the fstab record.
+//
+static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) {
+    int fs_stat = 0;
+
+    if (is_extfs(rec->fs_type)) {
+        struct ext4_super_block sb;
+
+        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+            if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
+                (sb.s_state & EXT4_VALID_FS) == 0) {
+                LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
+                      << "state flags: 0x" << std::hex << sb.s_state << ", "
+                      << "incompat feature flags: 0x" << std::hex << sb.s_feature_incompat;
+                fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
+            }
+
+            // Note: quotas should be enabled before running fsck.
+            tune_quota(blk_device, rec, &sb, &fs_stat);
         } else {
-            LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device;
-
-            int status = 0;
-            int ret = 0;
-            unsigned long reserved_blocks = 0;
-            android::base::unique_fd fd(
-                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
-            if (fd >= 0) {
-                struct ext4_super_block sb;
-                ret = read_super_block(fd, &sb);
-                if (ret < 0) {
-                    PERROR << "Can't read '" << blk_device << "' super block";
-                    return;
-                }
-                reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
-                unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
-                if (reserved_threshold < reserved_blocks) {
-                    LWARNING << "Reserved blocks " << reserved_blocks
-                             << " is too large";
-                    reserved_blocks = reserved_threshold;
-                }
-
-                if (ext4_r_blocks_count(&sb) == reserved_blocks) {
-                    LINFO << "Have reserved same blocks";
-                    return;
-                }
-            } else {
-                PERROR << "Failed to open '" << blk_device << "'";
-                return;
-            }
-
-            char buf[16] = {0};
-            snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
-            const char *tune2fs_argv[] = {
-                TUNE2FS_BIN,
-                buf,
-                blk_device,
-            };
-
-            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
-                                          const_cast<char **>(tune2fs_argv),
-                                          &status, true, LOG_KLOG | LOG_FILE,
-                                          true, NULL, NULL, 0);
-
-            if (ret < 0) {
-                /* No need to check for error in fork, we can't really handle it now */
-                LERROR << "Failed trying to run " << TUNE2FS_BIN;
-                *fs_stat |= FS_STAT_TUNE2FS_FAILED;
-            }
+            return fs_stat;
         }
     }
+
+    if ((rec->fs_mgr_flags & MF_CHECK) ||
+        (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
+        check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+    }
+
+    if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+        struct ext4_super_block sb;
+
+        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+            tune_reserved_size(blk_device, rec, &sb, &fs_stat);
+            tune_encrypt(blk_device, rec, &sb, &fs_stat);
+        }
+    }
+
+    return fs_stat;
 }
 
 static void remove_trailing_slashes(char *n)
@@ -457,6 +482,16 @@
     return rc;
 }
 
+// Orange state means the device is unlocked, see the following link for details.
+// https://source.android.com/security/verifiedboot/verified-boot#device_state
+bool fs_mgr_is_device_unlocked() {
+    std::string verified_boot_state;
+    if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+        return verified_boot_state == "orange";
+    }
+    return false;
+}
+
 /*
  * __mount(): wrapper around the mount() system call which also
  * sets the underlying block device to read-only if the mount is read-only.
@@ -476,10 +511,11 @@
         if ((info.st_mode & S_IFMT) == S_IFLNK)
             unlink(target);
     mkdir(target, 0755);
+    errno = 0;
     ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
     save_errno = errno;
-    LINFO << __FUNCTION__ << "(source=" << source << ",target="
-          << target << ",type=" << rec->fs_type << ")=" << ret;
+    PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
+          << ",type=" << rec->fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -507,15 +543,6 @@
     return ret;
 }
 
-static int device_is_force_encrypted() {
-    int ret = -1;
-    char value[PROP_VALUE_MAX];
-    ret = __system_property_get("ro.vold.forceencryption", value);
-    if (ret < 0)
-        return 0;
-    return strcmp(value, "1") ? 0 : 1;
-}
-
 /*
  * Tries to mount any of the consecutive fstab entries that match
  * the mountpoint of the one given by fstab->recs[start_idx].
@@ -559,10 +586,7 @@
                 continue;
             }
 
-            int fs_stat = 0;
-            int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
-                                                           fstab->recs[i].fs_type,
-                                                           &fstab->recs[i], &fs_stat);
+            int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
             if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
                 LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
                        << fstab->recs[i].mount_point << " rec[" << i
@@ -570,15 +594,6 @@
                 mount_errno = EINVAL;  // continue bootup for FDE
                 continue;
             }
-            if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
-                check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                         fstab->recs[i].mount_point, &fs_stat);
-            }
-
-            if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
-                do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                                 &fstab->recs[i], &fs_stat);
-            }
 
             int retry_count = 2;
             while (retry_count-- > 0) {
@@ -702,7 +717,9 @@
 
 static bool needs_block_encryption(const struct fstab_rec* rec)
 {
-    if (device_is_force_encrypted() && fs_mgr_is_encryptable(rec)) return true;
+    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) &&
+        fs_mgr_is_encryptable(rec))
+        return true;
     if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
     if (rec->fs_mgr_flags & MF_CRYPT) {
         /* Check for existence of convert_fde breadcrumb file */
@@ -756,36 +773,6 @@
     }
 }
 
-// TODO: add ueventd notifiers if they don't exist.
-// This is just doing a wait_for_device for maximum of 1s
-int fs_mgr_test_access(const char *device) {
-    int tries = 25;
-    while (tries--) {
-        if (!access(device, F_OK) || errno != ENOENT) {
-            return 0;
-        }
-        usleep(40 * 1000);
-    }
-    return -1;
-}
-
-bool is_device_secure() {
-    int ret = -1;
-    char value[PROP_VALUE_MAX];
-    ret = __system_property_get("ro.secure", value);
-    if (ret == 0) {
-#ifdef ALLOW_SKIP_SECURE_CHECK
-        // Allow eng builds to skip this check if the property
-        // is not readable (happens during early mount)
-        return false;
-#else
-        // If error and not an 'eng' build, we want to fail secure.
-        return true;
-#endif
-    }
-    return strcmp(value, "0") ? true : false;
-}
-
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
@@ -829,9 +816,7 @@
         }
 
         /* Translate LABEL= file system labels into block devices */
-        if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
-            !strcmp(fstab->recs[i].fs_type, "ext3") ||
-            !strcmp(fstab->recs[i].fs_type, "ext4")) {
+        if (is_extfs(fstab->recs[i].fs_type)) {
             int tret = translate_ext_labels(&fstab->recs[i]);
             if (tret < 0) {
                 LERROR << "Could not translate label to block device";
@@ -839,8 +824,10 @@
             }
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
-            wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+            !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+            LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
+            continue;
         }
 
         if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
@@ -858,7 +845,7 @@
                 /* Skips mounting the device. */
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
+        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
@@ -1047,22 +1034,12 @@
         }
 
         /* First check the filesystem if requested */
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
-            wait_for_file(n_blk_device, WAIT_TIMEOUT);
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+            LERROR << "Skipping mounting '" << n_blk_device << "'";
+            continue;
         }
 
-        int fs_stat = 0;
-        int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type,
-                                                       &fstab->recs[i], &fs_stat);
-
-        if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
-            check_fs(n_blk_device, fstab->recs[i].fs_type,
-                     fstab->recs[i].mount_point, &fs_stat);
-        }
-
-        if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
-            do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat);
-        }
+        int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
 
         if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
             if (!avb_handle) {
@@ -1079,7 +1056,7 @@
                 /* Skips mounting the device. */
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
+        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
@@ -1221,8 +1198,11 @@
             fclose(zram_fp);
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
-            wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+            !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+            LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
+            ret = -1;
+            continue;
         }
 
         /* Initialize the swap area */
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 2c99aa7..7824cfa 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -397,7 +397,7 @@
     fstab_entry->blk_device = strdup(verity_blk_name.c_str());
 
     // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
         return false;
     }
 
@@ -473,16 +473,6 @@
     return true;
 }
 
-// Orange state means the device is unlocked, see the following link for details.
-// https://source.android.com/security/verifiedboot/verified-boot#device_state
-static inline bool IsDeviceUnlocked() {
-    std::string verified_boot_state;
-    if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
-        return verified_boot_state == "orange";
-    }
-    return false;
-}
-
 FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
     FsManagerAvbOps avb_ops(fstab);
     return DoOpen(&avb_ops);
@@ -498,7 +488,7 @@
 }
 
 FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
-    bool is_device_unlocked = IsDeviceUnlocked();
+    bool is_device_unlocked = fs_mgr_is_device_unlocked();
 
     FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
     if (!avb_handle) {
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index ba1262f..43879fe 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -142,10 +142,8 @@
     }
     std::string path = iter->second;
 
-    // Ensures the device path (a symlink created by init) is ready to
-    // access. fs_mgr_test_access() will test a few iterations if the
-    // path doesn't exist yet.
-    if (fs_mgr_test_access(path.c_str()) < 0) {
+    // Ensures the device path (a symlink created by init) is ready to access.
+    if (!fs_mgr_wait_for_file(path, 1s)) {
         return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
     }
 
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index ab5beed..9c5d3f3 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <string>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -21,19 +23,11 @@
 
 #include "fs_mgr_priv.h"
 
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order).  returns 'true' if successfully found, 'false'
-// otherwise
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
     FS_MGR_CHECK(out_val != nullptr);
 
-    // first check if we have "ro.boot" property already
-    *out_val = android::base::GetProperty("ro.boot." + key, "");
-    if (!out_val->empty()) {
-        return true;
-    }
-
-    // fallback to kernel cmdline, properties may not be ready yet
     std::string cmdline;
     std::string cmdline_key("androidboot." + key);
     if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
@@ -48,12 +42,34 @@
         }
     }
 
+    return false;
+}
+
+// Tries to get the boot config value in properties, kernel cmdline and
+// device tree (in that order).  returns 'true' if successfully found, 'false'
+// otherwise
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+    FS_MGR_CHECK(out_val != nullptr);
+
+    // first check if we have "ro.boot" property already
+    *out_val = android::base::GetProperty("ro.boot." + key, "");
+    if (!out_val->empty()) {
+        return true;
+    }
+
+    // fallback to kernel cmdline, properties may not be ready yet
+    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+        return true;
+    }
+
     // lastly, check the device tree
     if (is_dt_compatible()) {
-        std::string file_name = kAndroidDtDir + "/" + key;
-        // DT entries terminate with '\0' but so do the properties
+        std::string file_name = get_android_dt_dir() + "/" + key;
         if (android::base::ReadFileToString(file_name, out_val)) {
-            return true;
+            if (!out_val->empty()) {
+                out_val->pop_back();  // Trims the trailing '\0' out.
+                return true;
+            }
         }
     }
 
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 6c527c5..92c6ee8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -29,6 +29,8 @@
 
 #include "fs_mgr_priv.h"
 
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
+
 struct fs_mgr_flag_values {
     char *key_loc;
     char* key_dir;
@@ -365,9 +367,26 @@
     return f;
 }
 
+static std::string init_android_dt_dir() {
+    std::string android_dt_dir;
+    // The platform may specify a custom Android DT path in kernel cmdline
+    if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
+        // Fall back to the standard procfs-based path
+        android_dt_dir = kDefaultAndroidDtDir;
+    }
+    return android_dt_dir;
+}
+
+// FIXME: The same logic is duplicated in system/core/init/
+const std::string& get_android_dt_dir() {
+    // Set once and saves time for subsequent calls to this function
+    static const std::string kAndroidDtDir = init_android_dt_dir();
+    return kAndroidDtDir;
+}
+
 static bool is_dt_fstab_compatible() {
     std::string dt_value;
-    std::string file_name = kAndroidDtDir + "/fstab/compatible";
+    std::string file_name = get_android_dt_dir() + "/fstab/compatible";
     if (read_dt_file(file_name, &dt_value)) {
         if (dt_value == "android,fstab") {
             return true;
@@ -383,7 +402,7 @@
         return fstab;
     }
 
-    std::string fstabdir_name = kAndroidDtDir + "/fstab";
+    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;
 
@@ -446,7 +465,7 @@
 }
 
 bool is_dt_compatible() {
-    std::string file_name = kAndroidDtDir + "/compatible";
+    std::string file_name = get_android_dt_dir() + "/compatible";
     std::string dt_value;
     if (read_dt_file(file_name, &dt_value)) {
         if (dt_value == "android,firmware") {
@@ -769,44 +788,19 @@
 }
 
 /*
- * Returns the 1st matching fstab_rec that follows the start_rec.
- * start_rec is the result of a previous search or NULL.
+ * Returns the fstab_rec* whose mount_point is path.
+ * Returns nullptr if not found.
  */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
-{
-    int i;
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
     if (!fstab) {
-        return NULL;
+        return nullptr;
     }
-
-    if (start_rec) {
-        for (i = 0; i < fstab->num_entries; i++) {
-            if (&fstab->recs[i] == start_rec) {
-                i++;
-                break;
-            }
-        }
-    } else {
-        i = 0;
-    }
-    for (; i < fstab->num_entries; i++) {
-        int len = strlen(fstab->recs[i].mount_point);
-        if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
-            (path[len] == '\0' || path[len] == '/')) {
+    for (int i = 0; i < fstab->num_entries; i++) {
+        if (fstab->recs[i].mount_point && path == fstab->recs[i].mount_point) {
             return &fstab->recs[i];
         }
     }
-    return NULL;
-}
-
-/*
- * Returns the 1st matching mount point.
- * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
- * and give the fstab_rec from the previous search.
- */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
-{
-    return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
+    return nullptr;
 }
 
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
@@ -864,32 +858,26 @@
     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
 }
 
-int fs_mgr_is_notrim(struct fstab_rec *fstab)
-{
+int fs_mgr_is_notrim(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_NOTRIM;
 }
 
-int fs_mgr_is_formattable(struct fstab_rec *fstab)
-{
+int fs_mgr_is_formattable(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & (MF_FORMATTABLE);
 }
 
-int fs_mgr_is_slotselect(struct fstab_rec *fstab)
-{
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_SLOTSELECT;
 }
 
-int fs_mgr_is_nofail(struct fstab_rec *fstab)
-{
+int fs_mgr_is_nofail(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_NOFAIL;
 }
 
-int fs_mgr_is_latemount(struct fstab_rec *fstab)
-{
+int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_LATEMOUNT;
 }
 
-int fs_mgr_is_quota(struct fstab_rec *fstab)
-{
+int fs_mgr_is_quota(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_QUOTA;
 }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 3ca507b..724156d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,8 +17,12 @@
 #ifndef __CORE_FS_MGR_PRIV_H
 #define __CORE_FS_MGR_PRIV_H
 
+#include <chrono>
+#include <string>
+
 #include <android-base/logging.h>
-#include <fs_mgr.h>
+
+#include "fs_mgr.h"
 #include "fs_mgr_priv_boot_config.h"
 
 /* The CHECK() in logging.h will use program invocation name as the tag.
@@ -43,8 +47,6 @@
 
 #define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
 
-#define WAIT_TIMEOUT 20
-
 /* fstab has the following format:
  *
  * Any line starting with a # is a comment and ignored
@@ -111,11 +113,15 @@
 
 #define DM_BUF_SIZE 4096
 
+using namespace std::chrono_literals;
+
 int fs_mgr_set_blk_ro(const char *blockdev);
-int fs_mgr_test_access(const char *device);
+bool fs_mgr_wait_for_file(const std::string& filename,
+                          const std::chrono::milliseconds relative_timeout);
 bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_is_device_unlocked();
+const std::string& get_android_dt_dir();
 bool is_dt_compatible();
-bool is_device_secure();
 int load_verity_state(struct fstab_rec* fstab, int* mode);
 
 #endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index 8773d33..d98dc02 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -20,8 +20,7 @@
 #include <sys/cdefs.h>
 #include <string>
 
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android");
-
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
 
 #endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 9ca15e2..33fd562 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -21,19 +21,12 @@
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
-// Returns "_a" or "_b" based on two possible values in kernel cmdline:
-//   - androidboot.slot = a or b OR
-//   - androidboot.slot_suffix = _a or _b
-// TODO: remove slot_suffix once it's deprecated.
+// Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
+// if that parameter does not exist.
 std::string fs_mgr_get_slot_suffix() {
-    std::string slot;
     std::string ab_suffix;
 
-    if (fs_mgr_get_boot_config("slot", &slot)) {
-        ab_suffix = "_" + slot;
-    } else if (!fs_mgr_get_boot_config("slot_suffix", &ab_suffix)) {
-        ab_suffix = "";
-    }
+    fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
     return ab_suffix;
 }
 
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8904995..896b603 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -348,10 +348,13 @@
 
 static int was_verity_restart()
 {
-    static const char *files[] = {
+    static const char* files[] = {
+        // clang-format off
+        "/sys/fs/pstore/console-ramoops-0",
         "/sys/fs/pstore/console-ramoops",
         "/proc/last_kmsg",
         NULL
+        // clang-format on
     };
     int i;
 
@@ -689,27 +692,55 @@
     return read_verity_state(fstab->verity_loc, offset, mode);
 }
 
-static void update_verity_table_blk_device(char *blk_device, char **table)
-{
-    std::string result, word;
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+//   verity table token:       /dev/block/bootdevice/by-name/vendor
+//   blk_device-1 (non-A/B):   /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device-2 (A/B):       /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+//   verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device:         /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+                                           bool slot_select) {
+    bool updated = false;
+    std::string result, ab_suffix;
     auto tokens = android::base::Split(*table, " ");
 
+    // If slot_select is set, it means blk_device is already updated with ab_suffix.
+    if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
     for (const auto& token : tokens) {
-        if (android::base::StartsWith(token, "/dev/block/") &&
-            android::base::StartsWith(blk_device, token.c_str())) {
-            word = blk_device;
+        std::string new_token;
+        if (android::base::StartsWith(token, "/dev/block/")) {
+            if (token == blk_device) return;  // no need to update if they're already the same.
+            std::size_t found1 = blk_device.find("by-name");
+            std::size_t found2 = token.find("by-name");
+            if (found1 != std::string::npos && found2 != std::string::npos &&
+                blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+                new_token = blk_device;
+            }
+        }
+
+        if (!new_token.empty()) {
+            updated = true;
+            LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+                  << "'";
         } else {
-            word = token;
+            new_token = token;
         }
 
         if (result.empty()) {
-            result = word;
+            result = new_token;
         } else {
-            result += " " + word;
+            result += " " + new_token;
         }
     }
 
-    if (result.empty()) {
+    if (!updated) {
         return;
     }
 
@@ -734,13 +765,6 @@
     const std::string mount_point(basename(fstab->mount_point));
     bool verified_at_boot = false;
 
-    // This is a public API and so deserves its own check to see if verity
-    // setup is needed at all.
-    if (!is_device_secure()) {
-        LINFO << "Verity setup skipped for " << mount_point;
-        return FS_MGR_SETUP_VERITY_SKIPPED;
-    }
-
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
             FEC_DEFAULT_ROOTS) < 0) {
         PERROR << "Failed to open '" << fstab->blk_device << "'";
@@ -751,8 +775,8 @@
     if (fec_verity_get_metadata(f, &verity) < 0) {
         PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
         // Allow verity disabled when the device is unlocked without metadata
-        if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) {
-            retval = FS_MGR_SETUP_VERITY_DISABLED;
+        if (fs_mgr_is_device_unlocked()) {
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
             LWARNING << "Allow invalid metadata when the device is unlocked";
         }
         goto out;
@@ -761,7 +785,7 @@
 #ifdef ALLOW_ADBD_DISABLE_VERITY
     if (verity.disabled) {
         retval = FS_MGR_SETUP_VERITY_DISABLED;
-        LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG";
+        LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG";
         goto out;
     }
 #endif
@@ -810,9 +834,15 @@
 
     // verify the signature on the table
     if (verify_verity_signature(verity) < 0) {
+        // Allow signature verification error when the device is unlocked
+        if (fs_mgr_is_device_unlocked()) {
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
+            LWARNING << "Allow signature verification error when the device is unlocked";
+            goto out;
+        }
         if (params.mode == VERITY_MODE_LOGGING) {
             // the user has been warned, allow mounting without dm-verity
-            retval = FS_MGR_SETUP_VERITY_SUCCESS;
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
             goto out;
         }
 
@@ -825,10 +855,9 @@
     LINFO << "Enabling dm-verity for " << mount_point.c_str()
           << " (mode " << params.mode << ")";
 
-    if (fstab->fs_mgr_flags & MF_SLOTSELECT) {
-        // Update the verity params using the actual block device path
-        update_verity_table_blk_device(fstab->blk_device, &params.table);
-    }
+    // Update the verity params using the actual block device path
+    update_verity_table_blk_device(fstab->blk_device, &params.table,
+                                   fstab->fs_mgr_flags & MF_SLOTSELECT);
 
     // load the verity mapping table
     if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
@@ -899,7 +928,7 @@
     }
 
     // make sure we've set everything up properly
-    if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
         goto out;
     }
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index e033d47..653d8fa 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,11 +22,7 @@
 #include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
-// C++ only headers
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-#include <string>
-#endif
+#include <fstab/fstab.h>
 
 // Magic number at start of verity metadata
 #define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
@@ -35,8 +31,6 @@
 // turn verity off in userdebug builds.
 #define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // "VOFF"
 
-__BEGIN_DECLS
-
 // Verity modes
 enum verity_mode {
     VERITY_MODE_EIO = 0,
@@ -53,49 +47,10 @@
     MOUNT_MODE_LATE = 2
 };
 
-/*
- * The entries must be kept in the same order as they were seen in the fstab.
- * Unless explicitly requested, a lookup on mount point should always
- * return the 1st one.
- */
-struct fstab {
-    int num_entries;
-    struct fstab_rec *recs;
-    char *fstab_filename;
-};
-
-struct fstab_rec {
-    char *blk_device;
-    char *mount_point;
-    char *fs_type;
-    unsigned long flags;
-    char *fs_options;
-    int fs_mgr_flags;
-    char *key_loc;
-    char* key_dir;
-    char *verity_loc;
-    long long length;
-    char *label;
-    int partnum;
-    int swap_prio;
-    int max_comp_streams;
-    unsigned int zram_size;
-    uint64_t reserved_size;
-    unsigned int file_contents_mode;
-    unsigned int file_names_mode;
-    unsigned int erase_blk_size;
-    unsigned int logical_blk_size;
-};
-
 // Callback function for verity status
 typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
         const char *mount_point, int mode, int status);
 
-struct fstab *fs_mgr_read_fstab_default();
-struct fstab *fs_mgr_read_fstab_dt();
-struct fstab *fs_mgr_read_fstab(const char *fstab_path);
-void fs_mgr_free_fstab(struct fstab *fstab);
-
 #define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
 #define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
@@ -120,28 +75,6 @@
 void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
 bool fs_mgr_load_verity_state(int* mode);
 bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
-int fs_mgr_add_entry(struct fstab *fstab,
-                     const char *mount_point, const char *fs_type,
-                     const char *blk_device);
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
-int fs_mgr_is_verified(const struct fstab_rec *fstab);
-int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab);
-int fs_mgr_is_avb(const struct fstab_rec *fstab);
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
-void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
-                                      const char **contents_mode_ret,
-                                      const char **filenames_mode_ret);
-int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
-int fs_mgr_is_notrim(struct fstab_rec *fstab);
-int fs_mgr_is_formattable(struct fstab_rec *fstab);
-int fs_mgr_is_slotselect(struct fstab_rec *fstab);
-int fs_mgr_is_nofail(struct fstab_rec *fstab);
-int fs_mgr_is_latemount(struct fstab_rec *fstab);
-int fs_mgr_is_quota(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
@@ -152,12 +85,4 @@
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
 int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
 
-__END_DECLS
-
-// C++ only functions
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
-std::string fs_mgr_get_slot_suffix();
-#endif
-
 #endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
new file mode 100644
index 0000000..94aacfd
--- /dev/null
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ * 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 __CORE_FS_TAB_H
+#define __CORE_FS_TAB_H
+
+#include <linux/dm-ioctl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string>
+
+/*
+ * The entries must be kept in the same order as they were seen in the fstab.
+ * Unless explicitly requested, a lookup on mount point should always
+ * return the 1st one.
+ */
+struct fstab {
+    int num_entries;
+    struct fstab_rec* recs;
+    char* fstab_filename;
+};
+
+struct fstab_rec {
+    char* blk_device;
+    char* mount_point;
+    char* fs_type;
+    unsigned long flags;
+    char* fs_options;
+    int fs_mgr_flags;
+    char* key_loc;
+    char* key_dir;
+    char* verity_loc;
+    long long length;
+    char* label;
+    int partnum;
+    int swap_prio;
+    int max_comp_streams;
+    unsigned int zram_size;
+    uint64_t reserved_size;
+    unsigned int file_contents_mode;
+    unsigned int file_names_mode;
+    unsigned int erase_blk_size;
+    unsigned int logical_blk_size;
+};
+
+struct fstab* fs_mgr_read_fstab_default();
+struct fstab* fs_mgr_read_fstab_dt();
+struct fstab* fs_mgr_read_fstab(const char* fstab_path);
+void fs_mgr_free_fstab(struct fstab* fstab);
+
+int fs_mgr_add_entry(struct fstab* fstab, const char* mount_point, const char* fs_type,
+                     const char* blk_device);
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path);
+int fs_mgr_is_voldmanaged(const struct fstab_rec* fstab);
+int fs_mgr_is_nonremovable(const struct fstab_rec* fstab);
+int fs_mgr_is_verified(const struct fstab_rec* fstab);
+int fs_mgr_is_verifyatboot(const struct fstab_rec* fstab);
+int fs_mgr_is_avb(const struct fstab_rec* fstab);
+int fs_mgr_is_encryptable(const struct fstab_rec* fstab);
+int fs_mgr_is_file_encrypted(const struct fstab_rec* fstab);
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec* fstab, const char** contents_mode_ret,
+                                      const char** filenames_mode_ret);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab);
+int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab);
+int fs_mgr_is_notrim(const struct fstab_rec* fstab);
+int fs_mgr_is_formattable(const struct fstab_rec* fstab);
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab);
+int fs_mgr_is_nofail(const struct fstab_rec* fstab);
+int fs_mgr_is_latemount(const struct fstab_rec* fstab);
+int fs_mgr_is_quota(const struct fstab_rec* fstab);
+
+std::string fs_mgr_get_slot_suffix();
+
+#endif /* __CORE_FS_TAB_H */
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index cb02a6f..2f4f4d7 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -26,11 +26,11 @@
 }
 
 #include <android-base/memory.h>
-#include <UniquePtr.h>
 #include <gatekeeper/gatekeeper.h>
 
 #include <iostream>
 #include <unordered_map>
+#include <memory>
 
 namespace gatekeeper {
 
@@ -173,7 +173,7 @@
     typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
     typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
 
-    UniquePtr<uint8_t[]> key_;
+    std::unique_ptr<uint8_t[]> key_;
     FailureRecordMap failure_map_;
     FastHashMap fast_hash_map_;
 };
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
index 3463c29..e3dc068 100644
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -19,7 +19,7 @@
 
 #include "SoftGateKeeper.h"
 
-#include <UniquePtr.h>
+#include <memory>
 
 using namespace gatekeeper;
 
@@ -68,7 +68,7 @@
             const uint8_t *provided_password, uint32_t provided_password_length,
             uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
 private:
-    UniquePtr<SoftGateKeeper> impl_;
+    std::unique_ptr<SoftGateKeeper> impl_;
 };
 
 } // namespace gatekeeper
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index c6369f9..d581736 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -23,6 +23,7 @@
 #include <inttypes.h>
 #include <stdint.h>
 #include <unistd.h>
+#include <memory>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -400,7 +401,7 @@
 
 private:
     sp<IGatekeeper> hw_device;
-    UniquePtr<SoftGateKeeperDevice> soft_device;
+    std::unique_ptr<SoftGateKeeperDevice> soft_device;
 
     bool clear_state_if_needed_done;
 };
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
index a62b1d4..c38c64b 100644
--- a/gatekeeperd/tests/Android.mk
+++ b/gatekeeperd/tests/Android.mk
@@ -19,7 +19,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := gatekeeperd-unit-tests
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
 LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
 LOCAL_STATIC_LIBRARIES := libscrypt_static
 LOCAL_C_INCLUDES := external/scrypt/lib/crypto
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
index 47a8bfa..100375f 100644
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -19,7 +19,6 @@
 
 #include <gtest/gtest.h>
 #include <hardware/hw_auth_token.h>
-#include <UniquePtr.h>
 
 #include "../SoftGateKeeper.h"
 
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index d26530b..676ee41 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -116,6 +116,10 @@
         { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
         { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
         { "Cold", BATTERY_HEALTH_COLD },
+        // battery health values from JEITA spec
+        { "Warm", BATTERY_HEALTH_GOOD },
+        { "Cool", BATTERY_HEALTH_GOOD },
+        { "Hot", BATTERY_HEALTH_OVERHEAT },
         { NULL, 0 },
     };
 
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index e7f3eaf..4f77e7a 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -18,7 +18,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -32,6 +31,7 @@
 #include <functional>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 
 #include <linux/netlink.h>
 #include <sys/socket.h>
@@ -72,8 +72,6 @@
 #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
 
-#define LAST_KMSG_PATH "/proc/last_kmsg"
-#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
 #define LAST_KMSG_MAX_SZ (32 * 1024)
 
 #define LOGE(x...) KLOG_ERROR("charger", x);
@@ -205,14 +203,21 @@
     LOGW("\n");
     LOGW("*************** LAST KMSG ***************\n");
     LOGW("\n");
-    buf = (char*)load_file(LAST_KMSG_PSTORE_PATH, &sz);
+    const char* kmsg[] = {
+        // clang-format off
+        "/sys/fs/pstore/console-ramoops-0",
+        "/sys/fs/pstore/console-ramoops",
+        "/proc/last_kmsg",
+        // clang-format on
+    };
+    for (size_t i = 0; i < arraysize(kmsg); ++i) {
+        buf = (char*)load_file(kmsg[i], &sz);
+        if (buf && sz) break;
+    }
 
     if (!buf || !sz) {
-        buf = (char*)load_file(LAST_KMSG_PATH, &sz);
-        if (!buf || !sz) {
-            LOGW("last_kmsg not found. Cold reset?\n");
-            goto out;
-        }
+        LOGW("last_kmsg not found. Cold reset?\n");
+        goto out;
     }
 
     len = min(sz, LAST_KMSG_MAX_SZ);
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
new file mode 100644
index 0000000..e9868a4
--- /dev/null
+++ b/include/private/fs_config.h
@@ -0,0 +1,4 @@
+// TODO(b/63135587) remove this file after the transitive dependency
+// from private/android_filesystem_config.h is resolved. All files that use
+// libcutils/include/private/fs_config.h should include the file directly, not
+// indirectly via private/android_filesystem_config.h.
diff --git a/init/Android.bp b/init/Android.bp
index 5d6d979..e906771 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -18,9 +18,8 @@
     name: "init_defaults",
     cpp_std: "experimental",
     sanitize: {
-        misc_undefined: ["integer"],
+        misc_undefined: ["signed-integer-overflow"],
     },
-    tidy_checks: ["-misc-forwarding-reference-overload"],
     cppflags: [
         "-DLOG_UEVENTS=0",
         "-Wall",
@@ -55,6 +54,9 @@
                 "-DSHUTDOWN_ZERO_TIMEOUT=1",
             ],
         },
+        uml: {
+            cppflags: ["-DUSER_MODE_LINUX"],
+        }
     },
 }
 
@@ -68,10 +70,18 @@
         "devices.cpp",
         "firmware_handler.cpp",
         "import_parser.cpp",
-        "init_parser.cpp",
         "log.cpp",
         "parser.cpp",
+        "persistent_properties.cpp",
+        "persistent_properties.proto",
+        "property_service.cpp",
+        "security.cpp",
+        "selinux.cpp",
         "service.cpp",
+        "subcontext.cpp",
+        "subcontext.proto",
+        "rlimit_parser.cpp",
+        "tokenizer.cpp",
         "uevent_listener.cpp",
         "ueventd_parser.cpp",
         "util.cpp",
@@ -82,7 +92,16 @@
         "libselinux",
         "liblog",
         "libprocessgroup",
+        "libfs_mgr",
+        "libprotobuf-cpp-lite",
     ],
+    include_dirs: [
+        "system/core/mkbootimg",
+    ],
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
 }
 
 /*
@@ -106,15 +125,11 @@
         "init.cpp",
         "init_first_stage.cpp",
         "keychords.cpp",
-        "property_service.cpp",
         "reboot.cpp",
-        "signal_handler.cpp",
+        "sigchld_handler.cpp",
         "ueventd.cpp",
         "watchdogd.cpp",
     ],
-    include_dirs: [
-        "system/core/mkbootimg"
-    ],
     static_libs: [
         "libinit",
         "libbootloader_message",
@@ -154,18 +169,44 @@
     defaults: ["init_defaults"],
     srcs: [
         "devices_test.cpp",
-        "init_parser_test.cpp",
         "init_test.cpp",
+        "persistent_properties_test.cpp",
         "property_service_test.cpp",
+        "result_test.cpp",
+        "rlimit_parser_test.cpp",
         "service_test.cpp",
+        "subcontext_test.cpp",
+        "ueventd_test.cpp",
         "util_test.cpp",
     ],
     shared_libs: [
         "libbase",
         "libcutils",
-        "libselinux",
     ],
-    static_libs: ["libinit"],
+    static_libs: [
+        "libinit",
+        "libselinux",
+        "libcrypto",
+        "libprotobuf-cpp-lite",
+    ],
+}
+
+cc_benchmark {
+    name: "init_benchmarks",
+    defaults: ["init_defaults"],
+    srcs: [
+        "subcontext_benchmark.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+    ],
+    static_libs: [
+        "libinit",
+        "libselinux",
+        "libcrypto",
+        "libprotobuf-cpp-lite",
+    ],
 }
 
 subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index bf75f5a..dd0f1bf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -9,12 +9,14 @@
     -DALLOW_LOCAL_PROP_OVERRIDE=1 \
     -DALLOW_PERMISSIVE_SELINUX=1 \
     -DREBOOT_BOOTLOADER_ON_PANIC=1 \
+    -DWORLD_WRITABLE_KMSG=1 \
     -DDUMP_ON_UMOUNT_FAILURE=1
 else
 init_options += \
     -DALLOW_LOCAL_PROP_OVERRIDE=0 \
     -DALLOW_PERMISSIVE_SELINUX=0 \
     -DREBOOT_BOOTLOADER_ON_PANIC=0 \
+    -DWORLD_WRITABLE_KMSG=0 \
     -DDUMP_ON_UMOUNT_FAILURE=0
 endif
 
@@ -38,8 +40,6 @@
 # --
 
 include $(CLEAR_VARS)
-# b/38002385, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
@@ -47,15 +47,12 @@
     init.cpp \
     init_first_stage.cpp \
     keychords.cpp \
-    property_service.cpp \
     reboot.cpp \
-    signal_handler.cpp \
+    sigchld_handler.cpp \
     ueventd.cpp \
     watchdogd.cpp \
 
 LOCAL_MODULE:= init
-LOCAL_C_INCLUDES += \
-    system/core/mkbootimg
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
@@ -73,6 +70,7 @@
     libcutils \
     libbase \
     libc \
+    libseccomp_policy \
     libselinux \
     liblog \
     libcrypto_utils \
@@ -84,6 +82,7 @@
     libprocessgroup \
     libavb \
     libkeyutils \
+    libprotobuf-cpp-lite \
 
 LOCAL_REQUIRED_MODULES := \
     e2fsdroid \
@@ -94,6 +93,5 @@
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
 
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
+LOCAL_SANITIZE := signed-integer-overflow
 include $(BUILD_EXECUTABLE)
diff --git a/init/README.md b/init/README.md
index 422fdad..d7edf21 100644
--- a/init/README.md
+++ b/init/README.md
@@ -216,6 +216,12 @@
   http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
   capabilities.
 
+`setrlimit <resource> <cur> <max>`
+> This applies the given rlimit to the service. rlimits are inherited by child
+  processes, so this effectively applies the given rlimit to the process tree
+  started by this service.
+  It is parsed similarly to the setrlimit command specified below.
+
 `seclabel <seclabel>`
 > Change to 'seclabel' before exec'ing this service.
   Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
@@ -260,6 +266,18 @@
 > Sets the child's /proc/self/oom\_score\_adj to the specified value,
   which must range from -1000 to 1000.
 
+`memcg.swappiness <value>`
+> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
+  which must be equal or greater than 0.
+
+`memcg.soft_limit_in_bytes <value>`
+> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
+  which must be equal or greater than 0.
+
+`memcg.limit_in_bytes <value>`
+> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
+  which must be equal or greater than 0.
+
 `shutdown <shutdown_behavior>`
 > Set shutdown behavior of the service process. When this is not specified,
   the service is killed during shutdown process by using SIGTERM and SIGKILL.
@@ -363,6 +381,11 @@
   within _argument_.
   Init halts executing commands until the forked process exits.
 
+`exec_background [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]`
+> Fork and execute command with the given arguments. This is handled similarly
+  to the `exec` command. The difference is that init does not halt executing
+  commands until the process exits for `exec_background`.
+
 `exec_start <service>`
 > Start a given service and halt the processing of additional init commands
   until it returns.  The command functions similarly to the `exec` command,
@@ -435,12 +458,20 @@
 `rmdir <path>`
 > Calls rmdir(2) on the given path.
 
+`readahead <file|dir> [--fully]`
+> Calls readahead(2) on the file or files within given directory.
+  Use option --fully to read the full file content.
+
 `setprop <name> <value>`
 > Set system property _name_ to _value_. Properties are expanded
   within _value_.
 
 `setrlimit <resource> <cur> <max>`
-> Set the rlimit for a resource.
+> Set the rlimit for a resource. This applies to all processes launched after
+  the limit is set. It is intended to be set early in init and applied globally.
+  _resource_ is best specified using its text representation ('cpu', 'rtio', etc
+  or 'RLIM_CPU', 'RLIM_RTIO', etc). It also may be specified as the int value
+  that the resource enum corresponds to.
 
 `start <service>`
 > Start a service running if it is not already running.
diff --git a/init/action.cpp b/init/action.cpp
index 41bd061..2617d00 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,59 +16,71 @@
 
 #include "action.h"
 
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 #include "util.h"
 
 using android::base::Join;
-using android::base::StringPrintf;
+using android::base::StartsWith;
 
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
-    : func_(f), args_(args), line_(line) {}
+namespace android {
+namespace init {
 
-int Command::InvokeFunc() const {
-    std::vector<std::string> expanded_args;
-    expanded_args.resize(args_.size());
-    expanded_args[0] = args_[0];
-    for (std::size_t i = 1; i < args_.size(); ++i) {
-        if (!expand_props(args_[i], &expanded_args[i])) {
-            LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
-            return -EINVAL;
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+                                   const std::vector<std::string>& args,
+                                   const std::string& context) {
+    auto builtin_arguments = BuiltinArguments(context);
+
+    builtin_arguments.args.resize(args.size());
+    builtin_arguments.args[0] = args[0];
+    for (std::size_t i = 1; i < args.size(); ++i) {
+        if (!expand_props(args[i], &builtin_arguments.args[i])) {
+            return Error() << "cannot expand '" << args[i] << "'";
         }
     }
 
-    return func_(expanded_args);
+    return function(builtin_arguments);
+}
+
+Command::Command(BuiltinFunction f, bool execute_in_subcontext,
+                 const std::vector<std::string>& args, int line)
+    : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
+
+Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
+    if (execute_in_subcontext_ && subcontext) {
+        return subcontext->Execute(args_);
+    } else {
+        const std::string& context = subcontext ? subcontext->context() : kInitContext;
+        return RunBuiltinFunction(func_, args_, context);
+    }
 }
 
 std::string Command::BuildCommandString() const {
     return Join(args_, ' ');
 }
 
-Action::Action(bool oneshot, const std::string& filename, int line)
-    : oneshot_(oneshot), filename_(filename), line_(line) {}
+Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line)
+    : oneshot_(oneshot), subcontext_(subcontext), filename_(filename), line_(line) {}
 
-const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+const KeywordFunctionMap* Action::function_map_ = nullptr;
 
-bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
+Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
     if (!function_map_) {
-        *err = "no function map available";
-        return false;
+        return Error() << "no function map available";
     }
 
-    auto function = function_map_->FindFunction(args, err);
-    if (!function) {
-        return false;
-    }
+    auto function = function_map_->FindFunction(args);
+    if (!function) return Error() << function.error();
 
-    AddCommand(function, args, line);
-    return true;
+    commands_.emplace_back(function->second, function->first, args, line);
+    return Success();
 }
 
 void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
-    commands_.emplace_back(f, args, line);
+    commands_.emplace_back(f, false, args, line);
 }
 
 std::size_t Action::NumCommands() const {
@@ -89,83 +101,86 @@
 }
 
 void Action::ExecuteCommand(const Command& command) const {
-    Timer t;
-    int result = command.InvokeFunc();
+    android::base::Timer t;
+    auto result = command.InvokeFunc(subcontext_);
+    auto duration = t.duration();
 
-    double duration_ms = t.duration_s() * 1000;
+    // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
+    // device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there
+    // are 198 such failures on bullhead.  Instead of spamming the log reporting them, we do not
+    // report such failures unless we're running at the DEBUG log level.
+    bool report_failure = !result.has_value();
+    if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
+        result.error_errno() == ENOENT) {
+        report_failure = false;
+    }
+
     // Any action longer than 50ms will be warned to user as slow operation
-    if (duration_ms > 50.0 ||
+    if (report_failure || duration > 50ms ||
         android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
-        std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
 
-        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
-                  << " returned " << result << " took " << duration_ms << "ms.";
+        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
+                  << ":" << command.line() << ") took " << duration.count() << "ms and "
+                  << (result ? "succeeded" : "failed: " + result.error_string());
     }
 }
 
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
+Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
     const static std::string prop_str("property:");
     std::string prop_name(trigger.substr(prop_str.length()));
     size_t equal_pos = prop_name.find('=');
     if (equal_pos == std::string::npos) {
-        *err = "property trigger found without matching '='";
-        return false;
+        return Error() << "property trigger found without matching '='";
     }
 
     std::string prop_value(prop_name.substr(equal_pos + 1));
     prop_name.erase(equal_pos);
 
     if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
-        *err = "multiple property triggers found for same property";
-        return false;
+        return Error() << "multiple property triggers found for same property";
     }
-    return true;
+    return Success();
 }
 
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Action::InitTriggers(const std::vector<std::string>& args) {
     const static std::string prop_str("property:");
     for (std::size_t i = 0; i < args.size(); ++i) {
         if (args[i].empty()) {
-            *err = "empty trigger is not valid";
-            return false;
+            return Error() << "empty trigger is not valid";
         }
 
         if (i % 2) {
             if (args[i] != "&&") {
-                *err = "&& is the only symbol allowed to concatenate actions";
-                return false;
+                return Error() << "&& is the only symbol allowed to concatenate actions";
             } else {
                 continue;
             }
         }
 
         if (!args[i].compare(0, prop_str.length(), prop_str)) {
-            if (!ParsePropertyTrigger(args[i], err)) {
-                return false;
+            if (auto result = ParsePropertyTrigger(args[i]); !result) {
+                return result;
             }
         } else {
             if (!event_trigger_.empty()) {
-                *err = "multiple event triggers are not allowed";
-                return false;
+                return Error() << "multiple event triggers are not allowed";
             }
 
             event_trigger_ = args[i];
         }
     }
 
-    return true;
+    return Success();
 }
 
-bool Action::InitSingleTrigger(const std::string& trigger) {
+Result<Success> Action::InitSingleTrigger(const std::string& trigger) {
     std::vector<std::string> name_vector{trigger};
-    std::string err;
-    bool ret = InitTriggers(name_vector, &err);
-    if (!ret) {
-        LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
+    if (auto result = InitTriggers(name_vector); !result) {
+        return Error() << "InitTriggers() failed: " << result.error();
     }
-    return ret;
+    return Success();
 }
 
 // This function checks that all property triggers are satisfied, that is
@@ -260,10 +275,11 @@
 }
 
 void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
-    auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
+    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0);
     std::vector<std::string> name_vector{name};
 
-    if (!action->InitSingleTrigger(name)) {
+    if (auto result = action->InitSingleTrigger(name); !result) {
+        LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error();
         return;
     }
 
@@ -332,25 +348,35 @@
     current_command_ = 0;
 }
 
-bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                int line, std::string* err) {
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
     if (triggers.size() < 1) {
-        *err = "actions must have a trigger";
-        return false;
+        return Error() << "Actions must have a trigger";
     }
 
-    auto action = std::make_unique<Action>(false, filename, line);
-    if (!action->InitTriggers(triggers, err)) {
-        return false;
+    Subcontext* action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+                action_subcontext = &subcontext;
+                break;
+            }
+        }
+    }
+
+    auto action = std::make_unique<Action>(false, action_subcontext, filename, line);
+
+    if (auto result = action->InitTriggers(triggers); !result) {
+        return Error() << "InitTriggers() failed: " << result.error();
     }
 
     action_ = std::move(action);
-    return true;
+    return Success();
 }
 
-bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    return action_ ? action_->AddCommand(std::move(args), line, err) : false;
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return action_ ? action_->AddCommand(std::move(args), line) : Success();
 }
 
 void ActionParser::EndSection() {
@@ -358,3 +384,6 @@
         action_manager_->AddAction(std::move(action_));
     }
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action.h b/init/action.h
index c04076a..cdfc6a0 100644
--- a/init/action.h
+++ b/init/action.h
@@ -24,20 +24,30 @@
 #include <vector>
 
 #include "builtins.h"
-#include "init_parser.h"
 #include "keyword_map.h"
+#include "parser.h"
+#include "result.h"
+#include "subcontext.h"
+
+namespace android {
+namespace init {
+
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+                                   const std::vector<std::string>& args, const std::string& context);
 
 class Command {
   public:
-    Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
+    Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+            int line);
 
-    int InvokeFunc() const;
+    Result<Success> InvokeFunc(Subcontext* subcontext) const;
     std::string BuildCommandString() const;
 
     int line() const { return line_; }
 
   private:
     BuiltinFunction func_;
+    bool execute_in_subcontext_;
     std::vector<std::string> args_;
     int line_;
 };
@@ -48,12 +58,12 @@
 
 class Action {
   public:
-    explicit Action(bool oneshot, const std::string& filename, int line);
+    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line);
 
-    bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+    Result<Success> AddCommand(const std::vector<std::string>& args, int line);
     void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
-    bool InitTriggers(const std::vector<std::string>& args, std::string* err);
-    bool InitSingleTrigger(const std::string& trigger);
+    Result<Success> InitTriggers(const std::vector<std::string>& args);
+    Result<Success> InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
@@ -66,24 +76,24 @@
     bool oneshot() const { return oneshot_; }
     const std::string& filename() const { return filename_; }
     int line() const { return line_; }
-    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+    static void set_function_map(const KeywordFunctionMap* function_map) {
         function_map_ = function_map;
     }
 
-
-private:
+  private:
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
-    bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+    Result<Success> ParsePropertyTrigger(const std::string& trigger);
 
     std::map<std::string, std::string> property_triggers_;
     std::string event_trigger_;
     std::vector<Command> commands_;
     bool oneshot_;
+    Subcontext* subcontext_;
     std::string filename_;
     int line_;
-    static const KeywordMap<BuiltinFunction>* function_map_;
+    static const KeywordFunctionMap* function_map_;
 };
 
 class ActionManager {
@@ -115,16 +125,20 @@
 
 class ActionParser : public SectionParser {
   public:
-    ActionParser(ActionManager* action_manager)
-        : action_manager_(action_manager), action_(nullptr) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
+        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
     void EndSection() override;
 
   private:
     ActionManager* action_manager_;
+    std::vector<Subcontext>* subcontexts_;
     std::unique_ptr<Action> action_;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 825603a..379b4fa 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -40,6 +40,9 @@
 using android::base::StringPrintf;
 using namespace std::chrono_literals;
 
+namespace android {
+namespace init {
+
 static std::thread* g_bootcharting_thread;
 
 static std::mutex g_bootcharting_finished_mutex;
@@ -160,35 +163,38 @@
   LOG(INFO) << "Bootcharting finished";
 }
 
-static int do_bootchart_start() {
-  // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
-  std::string start;
-  if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
-    LOG(VERBOSE) << "Not bootcharting";
-    return 0;
-  }
+static Result<Success> do_bootchart_start() {
+    // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+    std::string start;
+    if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+        LOG(VERBOSE) << "Not bootcharting";
+        return Success();
+    }
 
-  g_bootcharting_thread = new std::thread(bootchart_thread_main);
-  return 0;
+    g_bootcharting_thread = new std::thread(bootchart_thread_main);
+    return Success();
 }
 
-static int do_bootchart_stop() {
-  if (!g_bootcharting_thread) return 0;
+static Result<Success> do_bootchart_stop() {
+    if (!g_bootcharting_thread) return Success();
 
-  // Tell the worker thread it's time to quit.
-  {
-    std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
-    g_bootcharting_finished = true;
-    g_bootcharting_finished_cv.notify_one();
-  }
+    // Tell the worker thread it's time to quit.
+    {
+        std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+        g_bootcharting_finished = true;
+        g_bootcharting_finished_cv.notify_one();
+    }
 
-  g_bootcharting_thread->join();
-  delete g_bootcharting_thread;
-  g_bootcharting_thread = nullptr;
-  return 0;
+    g_bootcharting_thread->join();
+    delete g_bootcharting_thread;
+    g_bootcharting_thread = nullptr;
+    return Success();
 }
 
-int do_bootchart(const std::vector<std::string>& args) {
-  if (args[1] == "start") return do_bootchart_start();
-  return do_bootchart_stop();
+Result<Success> do_bootchart(const BuiltinArguments& args) {
+    if (args[1] == "start") return do_bootchart_start();
+    return do_bootchart_stop();
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/bootchart.h b/init/bootchart.h
index 0e3593d..05474ca 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,6 +20,15 @@
 #include <string>
 #include <vector>
 
-int do_bootchart(const std::vector<std::string>& args);
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> do_bootchart(const BuiltinArguments& args);
+
+}  // namespace init
+}  // namespace android
 
 #endif /* _BOOTCHART_H */
diff --git a/init/builtin_arguments.h b/init/builtin_arguments.h
new file mode 100644
index 0000000..1742b78
--- /dev/null
+++ b/init/builtin_arguments.h
@@ -0,0 +1,43 @@
+/*
+ * 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_BUILTIN_ARGUMENTS_H
+#define _INIT_BUILTIN_ARGUMENTS_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+struct BuiltinArguments {
+    BuiltinArguments(const std::string& context) : context(context) {}
+    BuiltinArguments(std::vector<std::string> args, const std::string& context)
+        : args(std::move(args)), context(context) {}
+
+    const std::string& operator[](std::size_t i) const { return args[i]; }
+    auto begin() const { return args.begin(); }
+    auto end() const { return args.end(); }
+    auto size() const { return args.size(); }
+
+    std::vector<std::string> args;
+    const std::string& context;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index fcc6092..950a551 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <linux/loop.h>
 #include <linux/module.h>
 #include <mntent.h>
@@ -39,12 +40,14 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/ext4_crypt.h>
@@ -53,145 +56,168 @@
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
+#include <system/thread_defs.h>
 
 #include "action.h"
 #include "bootchart.h"
 #include "init.h"
-#include "init_parser.h"
+#include "parser.h"
 #include "property_service.h"
 #include "reboot.h"
+#include "rlimit_parser.h"
 #include "service.h"
-#include "signal_handler.h"
+#include "subcontext.h"
 #include "util.h"
 
 using namespace std::literals::string_literals;
 
+using android::base::unique_fd;
+
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
 
+namespace android {
+namespace init {
+
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
-static int insmod(const char *filename, const char *options, int flags) {
-    int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
-    if (fd == -1) {
-        PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
-        return -1;
-    }
-    int rc = syscall(__NR_finit_module, fd, options, flags);
-    if (rc == -1) {
-        PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
-    }
-    close(fd);
-    return rc;
-}
-
-static int __ifupdown(const char *interface, int up) {
-    struct ifreq ifr;
-    int s, ret;
-
-    strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
-
-    s = socket(AF_INET, SOCK_DGRAM, 0);
-    if (s < 0)
-        return -1;
-
-    ret = ioctl(s, SIOCGIFFLAGS, &ifr);
-    if (ret < 0) {
-        goto done;
-    }
-
-    if (up)
-        ifr.ifr_flags |= IFF_UP;
-    else
-        ifr.ifr_flags &= ~IFF_UP;
-
-    ret = ioctl(s, SIOCSIFFLAGS, &ifr);
-
-done:
-    close(s);
-    return ret;
-}
-
-static int reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
     std::string err;
     if (!write_bootloader_message(options, &err)) {
-        LOG(ERROR) << "failed to set bootloader message: " << err;
-        return -1;
+        return Error() << "Failed to set bootloader message: " << err;
     }
     property_set("sys.powerctl", "reboot,recovery");
-    return 0;
+    return Success();
 }
 
-static int do_class_start(const std::vector<std::string>& args) {
-        /* Starting a class does not start services
-         * which are explicitly disabled.  They must
-         * be started individually.
-         */
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
-    return 0;
-}
-
-static int do_class_stop(const std::vector<std::string>& args) {
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
-    return 0;
-}
-
-static int do_class_reset(const std::vector<std::string>& args) {
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
-    return 0;
-}
-
-static int do_class_restart(const std::vector<std::string>& args) {
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->Restart(); });
-    return 0;
-}
-
-static int do_domainname(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+template <typename F>
+static void ForEachServiceInClass(const std::string& classname, F function) {
+    for (const auto& service : ServiceList::GetInstance()) {
+        if (service->classnames().count(classname)) std::invoke(function, service);
     }
-    return 0;
 }
 
-static int do_enable(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        return -1;
+static Result<Success> do_class_start(const BuiltinArguments& args) {
+    // Starting a class does not start services which are explicitly disabled.
+    // They must  be started individually.
+    for (const auto& service : ServiceList::GetInstance()) {
+        if (service->classnames().count(args[1])) {
+            if (auto result = service->StartIfNotDisabled(); !result) {
+                LOG(ERROR) << "Could not start service '" << service->name()
+                           << "' as part of class '" << args[1] << "': " << result.error();
+            }
+        }
     }
-    return svc->Enable();
+    return Success();
 }
 
-static int do_exec(const std::vector<std::string>& args) {
-    return ServiceManager::GetInstance().Exec(args) ? 0 : -1;
+static Result<Success> do_class_stop(const BuiltinArguments& args) {
+    ForEachServiceInClass(args[1], &Service::Stop);
+    return Success();
 }
 
-static int do_exec_start(const std::vector<std::string>& args) {
-    return ServiceManager::GetInstance().ExecStart(args[1]) ? 0 : -1;
+static Result<Success> do_class_reset(const BuiltinArguments& args) {
+    ForEachServiceInClass(args[1], &Service::Reset);
+    return Success();
 }
 
-static int do_export(const std::vector<std::string>& args) {
-    return add_environment(args[1].c_str(), args[2].c_str());
+static Result<Success> do_class_restart(const BuiltinArguments& args) {
+    ForEachServiceInClass(args[1], &Service::Restart);
+    return Success();
 }
 
-static int do_hostname(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_domainname(const BuiltinArguments& args) {
+    if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
+        return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
     }
-    return 0;
+    return Success();
 }
 
-static int do_ifup(const std::vector<std::string>& args) {
-    return __ifupdown(args[1].c_str(), 1);
+static Result<Success> do_enable(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "Could not find service";
+
+    if (auto result = svc->Enable(); !result) {
+        return Error() << "Could not enable service: " << result.error();
+    }
+
+    return Success();
 }
 
-static int do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_exec(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
+    if (!service) {
+        return Error() << "Could not create exec service";
+    }
+    if (auto result = service->ExecStart(); !result) {
+        return Error() << "Could not start exec service: " << result.error();
+    }
+
+    ServiceList::GetInstance().AddService(std::move(service));
+    return Success();
+}
+
+static Result<Success> do_exec_background(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
+    if (!service) {
+        return Error() << "Could not create exec background service";
+    }
+    if (auto result = service->Start(); !result) {
+        return Error() << "Could not start exec background service: " << result.error();
+    }
+
+    ServiceList::GetInstance().AddService(std::move(service));
+    return Success();
+}
+
+static Result<Success> do_exec_start(const BuiltinArguments& args) {
+    Service* service = ServiceList::GetInstance().FindService(args[1]);
+    if (!service) {
+        return Error() << "Service not found";
+    }
+
+    if (auto result = service->ExecStart(); !result) {
+        return Error() << "Could not start exec service: " << result.error();
+    }
+
+    return Success();
+}
+
+static Result<Success> do_export(const BuiltinArguments& args) {
+    if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
+        return ErrnoError() << "setenv() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_hostname(const BuiltinArguments& args) {
+    if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
+        return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
+    }
+    return Success();
+}
+
+static Result<Success> do_ifup(const BuiltinArguments& args) {
+    struct ifreq ifr;
+
+    strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
+
+    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+    if (s < 0) return ErrnoError() << "opening socket failed";
+
+    if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+        return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
+    }
+
+    ifr.ifr_flags |= IFF_UP;
+
+    if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
+        return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
+    }
+
+    return Success();
+}
+
+static Result<Success> do_insmod(const BuiltinArguments& args) {
     int flags = 0;
     auto it = args.begin() + 1;
 
@@ -202,53 +228,56 @@
 
     std::string filename = *it++;
     std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
-    return insmod(filename.c_str(), options.c_str(), flags);
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed";
+
+    int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
+    if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
+
+    return Success();
 }
 
-static int do_mkdir(const std::vector<std::string>& args) {
+// mkdir <path> [mode] [owner] [group]
+static Result<Success> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
-    int ret;
-
-    /* mkdir <path> [mode] [owner] [group] */
-
     if (args.size() >= 3) {
         mode = std::strtoul(args[2].c_str(), 0, 8);
     }
 
-    ret = make_dir(args[1].c_str(), mode, sehandle);
-    /* chmod in case the directory already exists */
-    if (ret == -1 && errno == EEXIST) {
-        ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
-    }
-    if (ret == -1) {
-        return -errno;
+    if (!make_dir(args[1], mode)) {
+        /* chmod in case the directory already exists */
+        if (errno == EEXIST) {
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat() failed";
+            }
+        } else {
+            return ErrnoError() << "mkdir() failed";
+        }
     }
 
     if (args.size() >= 4) {
-        uid_t uid;
-        std::string decode_uid_err;
-        if (!DecodeUid(args[3], &uid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find UID for '" << args[3] << "': " << decode_uid_err;
-            return -1;
+        auto uid = DecodeUid(args[3]);
+        if (!uid) {
+            return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
         }
-        gid_t gid = -1;
+        Result<gid_t> gid = -1;
 
         if (args.size() == 5) {
-            if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
-                LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
-                return -1;
+            gid = DecodeUid(args[4]);
+            if (!gid) {
+                return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
             }
         }
 
-        if (lchown(args[1].c_str(), uid, gid) == -1) {
-            return -errno;
+        if (lchown(args[1].c_str(), *uid, *gid) == -1) {
+            return ErrnoError() << "lchown failed";
         }
 
         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
         if (mode & (S_ISUID | S_ISGID)) {
-            ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
-            if (ret == -1) {
-                return -errno;
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat failed";
             }
         }
     }
@@ -259,15 +288,18 @@
                 "--prompt_and_wipe_data",
                 "--reason=set_policy_failed:"s + args[1]};
             reboot_into_recovery(options);
-            return -1;
+            return Success();
         }
     }
-    return 0;
+    return Success();
 }
 
 /* umount <path> */
-static int do_umount(const std::vector<std::string>& args) {
-  return umount(args[1].c_str());
+static Result<Success> do_umount(const BuiltinArguments& args) {
+    if (umount(args[1].c_str()) < 0) {
+        return ErrnoError() << "umount() failed";
+    }
+    return Success();
 }
 
 static struct {
@@ -295,16 +327,13 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-static int do_mount(const std::vector<std::string>& args) {
-    char tmp[64];
-    const char *source, *target, *system;
-    const char *options = NULL;
+static Result<Success> do_mount(const BuiltinArguments& args) {
+    const char* options = nullptr;
     unsigned flags = 0;
-    std::size_t na = 0;
-    int n, i;
-    int wait = 0;
+    bool wait = false;
 
-    for (na = 4; na < args.size(); na++) {
+    for (size_t na = 4; na < args.size(); na++) {
+        size_t i;
         for (i = 0; mount_flags[i].name; i++) {
             if (!args[na].compare(mount_flags[i].name)) {
                 flags |= mount_flags[i].flag;
@@ -313,71 +342,54 @@
         }
 
         if (!mount_flags[i].name) {
-            if (!args[na].compare("wait"))
-                wait = 1;
-            /* if our last argument isn't a flag, wolf it up as an option string */
-            else if (na + 1 == args.size())
+            if (!args[na].compare("wait")) {
+                wait = true;
+                // If our last argument isn't a flag, wolf it up as an option string.
+            } else if (na + 1 == args.size()) {
                 options = args[na].c_str();
+            }
         }
     }
 
-    system = args[1].c_str();
-    source = args[2].c_str();
-    target = args[3].c_str();
+    const char* system = args[1].c_str();
+    const char* source = args[2].c_str();
+    const char* target = args[3].c_str();
 
-    if (!strncmp(source, "loop@", 5)) {
-        int mode, loop, fd;
-        struct loop_info info;
+    if (android::base::StartsWith(source, "loop@")) {
+        int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
+        unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
+        if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
 
-        mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
-        fd = open(source + 5, mode | O_CLOEXEC);
-        if (fd < 0) {
-            return -1;
-        }
+        for (size_t n = 0;; n++) {
+            std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
+            unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
+            if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
 
-        for (n = 0; ; n++) {
-            snprintf(tmp, sizeof(tmp), "/dev/block/loop%d", n);
-            loop = open(tmp, mode | O_CLOEXEC);
-            if (loop < 0) {
-                close(fd);
-                return -1;
-            }
-
+            loop_info info;
             /* if it is a blank loop device */
             if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
                 /* if it becomes our loop device */
-                if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
-                    close(fd);
-
-                    if (mount(tmp, target, system, flags, options) < 0) {
+                if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
+                    if (mount(tmp.c_str(), target, system, flags, options) < 0) {
                         ioctl(loop, LOOP_CLR_FD, 0);
-                        close(loop);
-                        return -1;
+                        return ErrnoError() << "mount() failed";
                     }
-
-                    close(loop);
-                    goto exit_success;
+                    return Success();
                 }
             }
-
-            close(loop);
         }
 
-        close(fd);
-        LOG(ERROR) << "out of loopback devices";
-        return -1;
+        return Error() << "out of loopback devices";
     } else {
         if (wait)
             wait_for_file(source, kCommandRetryTimeout);
         if (mount(source, target, system, flags, options) < 0) {
-            return -1;
+            return ErrnoError() << "mount() failed";
         }
 
     }
 
-exit_success:
-    return 0;
-
+    return Success();
 }
 
 /* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -385,21 +397,15 @@
  * start_index: index of the first path in the args list
  */
 static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
-    Parser& parser = Parser::GetInstance();
+    auto& action_manager = ActionManager::GetInstance();
+    auto& service_list = ServiceList::GetInstance();
+    Parser parser = CreateParser(action_manager, service_list);
     if (end_index <= start_index) {
         // Fallbacks for partitions on which early mount isn't enabled.
-        if (!parser.is_system_etc_init_loaded()) {
-            parser.ParseConfig("/system/etc/init");
-            parser.set_is_system_etc_init_loaded(true);
+        for (const auto& path : late_import_paths) {
+            parser.ParseConfig(path);
         }
-        if (!parser.is_vendor_etc_init_loaded()) {
-            parser.ParseConfig("/vendor/etc/init");
-            parser.set_is_vendor_etc_init_loaded(true);
-        }
-        if (!parser.is_odm_etc_init_loaded()) {
-            parser.ParseConfig("/odm/etc/init");
-            parser.set_is_odm_etc_init_loaded(true);
-        }
+        late_import_paths.clear();
     } else {
         for (size_t i = start_index; i < end_index; ++i) {
             parser.ParseConfig(args[i]);
@@ -415,9 +421,7 @@
  *
  *  Call fs_mgr_mount_all() to mount the given fstab
  */
-static int mount_fstab(const char* fstabfile, int mount_mode) {
-    int ret = -1;
-
+static Result<int> mount_fstab(const char* fstabfile, int mount_mode) {
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -435,9 +439,9 @@
         }
 
         if (WIFEXITED(status)) {
-            ret = WEXITSTATUS(status);
+            return WEXITSTATUS(status);
         } else {
-            ret = -1;
+            return Error() << "child aborted";
         }
     } else if (pid == 0) {
         /* child, call fs_mgr_mount_all() */
@@ -454,10 +458,8 @@
         }
         _exit(child_ret);
     } else {
-        /* fork failed, return an error */
-        return -1;
+        return Error() << "fork() failed";
     }
-    return ret;
 }
 
 /* Queue event based on fs_mgr return code.
@@ -469,29 +471,33 @@
  *
  * return code is processed based on input code
  */
-static int queue_fs_event(int code) {
-    int ret = code;
+static Result<Success> queue_fs_event(int code) {
     if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "block");
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         property_set("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
-        ret = reboot_into_recovery(options);
+        reboot_into_recovery(options);
+        return Success();
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
-            return -1;
+            return Error() << "e4crypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -499,29 +505,32 @@
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
-            return -1;
+            return Error() << "e4crypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
 
         // defaultcrypto detects file/block encryption. init flow is same for each.
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
         if (e4crypt_install_keyring()) {
-            return -1;
+            return Error() << "e4crypt_install_keyring() failed";
         }
         property_set("ro.crypto.type", "file");
 
         // encrypt detects file/block encryption. init flow is same for each.
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
+        return Success();
     } else if (code > 0) {
-        PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
+        Error() << "fs_mgr_mount_all() returned unexpected error " << code;
     }
     /* else ... < 0: error */
 
-    return ret;
+    return Error() << "Invalid code: " << code;
 }
 
 /* mount_all <fstab> [ <path> ]* [--<options>]*
@@ -529,7 +538,7 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-static int do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const BuiltinArguments& args) {
     std::size_t na = 0;
     bool import_rc = true;
     bool queue_event = true;
@@ -552,27 +561,32 @@
         }
     }
 
-    std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s",
-                                                        prop_post_fix);
-    Timer t;
-    int ret =  mount_fstab(fstabfile, mount_mode);
-    property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str());
+    std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
+    android::base::Timer t;
+    auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+    if (!mount_fstab_return_code) {
+        return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+    }
+    property_set(prop_name, std::to_string(t.duration().count()));
 
     if (import_rc) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
-        import_late(args, 2, path_arg_end);
+        import_late(args.args, 2, path_arg_end);
     }
 
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        ret = queue_fs_event(ret);
+        auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+        if (!queue_fs_result) {
+            return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
+        }
     }
 
-    return ret;
+    return Success();
 }
 
-static int do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const BuiltinArguments& args) {
     struct fstab *fstab;
     int ret;
 
@@ -580,148 +594,238 @@
     ret = fs_mgr_swapon_all(fstab);
     fs_mgr_free_fstab(fstab);
 
-    return ret;
+    if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
+    return Success();
 }
 
-static int do_setprop(const std::vector<std::string>& args) {
-    const char* name = args[1].c_str();
-    const char* value = args[2].c_str();
-    property_set(name, value);
-    return 0;
+static Result<Success> do_setprop(const BuiltinArguments& args) {
+    property_set(args[1], args[2]);
+    return Success();
 }
 
-static int do_setrlimit(const std::vector<std::string>& args) {
-    struct rlimit limit;
-    int resource;
-    if (android::base::ParseInt(args[1], &resource) &&
-        android::base::ParseUint(args[2], &limit.rlim_cur) &&
-        android::base::ParseUint(args[3], &limit.rlim_max)) {
-        return setrlimit(resource, &limit);
+static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+    auto rlimit = ParseRlimit(args.args);
+    if (!rlimit) return rlimit.error();
+
+    if (setrlimit(rlimit->first, &rlimit->second) == -1) {
+        return ErrnoError() << "setrlimit failed";
     }
-    LOG(WARNING) << "ignoring setrlimit " << args[1] << " " << args[2] << " " << args[3];
-    return -1;
+    return Success();
 }
 
-static int do_start(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_start: Service " << args[1] << " not found";
-        return -1;
+static Result<Success> do_start(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "service " << args[1] << " not found";
+    if (auto result = svc->Start(); !result) {
+        return Error() << "Could not start service: " << result.error();
     }
-    if (!svc->Start())
-        return -1;
-    return 0;
+    return Success();
 }
 
-static int do_stop(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
-        return -1;
-    }
+static Result<Success> do_stop(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
-    return 0;
+    return Success();
 }
 
-static int do_restart(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
-        return -1;
-    }
+static Result<Success> do_restart(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
-    return 0;
+    return Success();
 }
 
-static int do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
-    return 0;
+    return Success();
 }
 
-static int do_symlink(const std::vector<std::string>& args) {
-    return symlink(args[1].c_str(), args[2].c_str());
-}
-
-static int do_rm(const std::vector<std::string>& args) {
-    return unlink(args[1].c_str());
-}
-
-static int do_rmdir(const std::vector<std::string>& args) {
-    return rmdir(args[1].c_str());
-}
-
-static int do_sysclktz(const std::vector<std::string>& args) {
-    struct timezone tz = {};
-    if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
-        return 0;
+static Result<Success> do_symlink(const BuiltinArguments& args) {
+    if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
+        // The symlink builtin is often used to create symlinks for older devices to be backwards
+        // compatible with new paths, therefore we skip reporting this error.
+        if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
+            return Success();
+        }
+        return ErrnoError() << "symlink() failed";
     }
-    return -1;
+    return Success();
 }
 
-static int do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const BuiltinArguments& args) {
+    if (unlink(args[1].c_str()) < 0) {
+        return ErrnoError() << "unlink() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_rmdir(const BuiltinArguments& args) {
+    if (rmdir(args[1].c_str()) < 0) {
+        return ErrnoError() << "rmdir() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_sysclktz(const BuiltinArguments& args) {
+    struct timezone tz = {};
+    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+        return Error() << "Unable to parse mins_west_of_gmt";
+    }
+
+    if (settimeofday(nullptr, &tz) == -1) {
+        return ErrnoError() << "settimeofday() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
     int mode = -1;
     bool loaded = fs_mgr_load_verity_state(&mode);
     if (loaded && mode != VERITY_MODE_DEFAULT) {
         ActionManager::GetInstance().QueueEventTrigger("verity-logging");
     }
-    return loaded ? 0 : 1;
+    if (!loaded) return Error() << "Could not load verity state";
+
+    return Success();
 }
 
 static void verity_update_property(fstab_rec *fstab, const char *mount_point,
                                    int mode, int status) {
-    property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
-                 android::base::StringPrintf("%d", mode).c_str());
+    property_set("partition."s + mount_point + ".verified", std::to_string(mode));
 }
 
-static int do_verity_update_state(const std::vector<std::string>& args) {
-    return fs_mgr_update_verity_state(verity_update_property) ? 0 : 1;
-}
-
-static int do_write(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile(args[1], args[2], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
+    if (!fs_mgr_update_verity_state(verity_update_property)) {
+        return Error() << "fs_mgr_update_verity_state() failed";
     }
-    return 0;
+    return Success();
 }
 
-static int do_copy(const std::vector<std::string>& args) {
-    std::string data;
-    std::string err;
-    if (!ReadFile(args[1], &data, &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_write(const BuiltinArguments& args) {
+    if (auto result = WriteFile(args[1], args[2]); !result) {
+        return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
     }
-    if (!WriteFile(args[2], data, &err)) {
-        LOG(ERROR) << err;
-        return -1;
-    }
-    return 0;
+
+    return Success();
 }
 
-static int do_chown(const std::vector<std::string>& args) {
-    uid_t uid;
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &uid, &decode_uid_err)) {
-        LOG(ERROR) << "Unable to find UID for '" << args[1] << "': " << decode_uid_err;
-        return -1;
+static Result<Success> readahead_file(const std::string& filename, bool fully) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
+    if (fd == -1) {
+        return ErrnoError() << "Error opening file";
+    }
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED)) {
+        return ErrnoError() << "Error posix_fadvise file";
+    }
+    if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+        return ErrnoError() << "Error readahead file";
+    }
+    if (fully) {
+        char buf[BUFSIZ];
+        ssize_t n;
+        while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+        }
+        if (n != 0) {
+            return ErrnoError() << "Error reading file";
+        }
+    }
+    return Success();
+}
+
+static Result<Success> do_readahead(const BuiltinArguments& args) {
+    struct stat sb;
+
+    if (stat(args[1].c_str(), &sb)) {
+        return ErrnoError() << "Error opening " << args[1];
+    }
+
+    bool readfully = false;
+    if (args.size() == 3 && args[2] == "--fully") {
+        readfully = true;
+    }
+    // We will do readahead in a forked process in order not to block init
+    // since it may block while it reads the
+    // filesystem metadata needed to locate the requested blocks.  This
+    // occurs frequently with ext[234] on large files using indirect blocks
+    // instead of extents, giving the appearance that the call blocks until
+    // the requested data has been read.
+    pid_t pid = fork();
+    if (pid == 0) {
+        if (setpriority(PRIO_PROCESS, 0, static_cast<int>(ANDROID_PRIORITY_LOWEST)) != 0) {
+            PLOG(WARNING) << "setpriority failed";
+        }
+        if (android_set_ioprio(0, IoSchedClass_IDLE, 7)) {
+            PLOG(WARNING) << "ioprio_get failed";
+        }
+        android::base::Timer t;
+        if (S_ISREG(sb.st_mode)) {
+            if (auto result = readahead_file(args[1], readfully); !result) {
+                LOG(WARNING) << "Unable to readahead '" << args[1] << "': " << result.error();
+                _exit(EXIT_FAILURE);
+            }
+        } else if (S_ISDIR(sb.st_mode)) {
+            char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
+            std::unique_ptr<FTS, decltype(&fts_close)> fts(
+                fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
+            if (!fts) {
+                PLOG(ERROR) << "Error opening directory: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+            // Traverse the entire hierarchy and do readahead
+            for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
+                 ftsent = fts_read(fts.get())) {
+                if (ftsent->fts_info & FTS_F) {
+                    const std::string filename = ftsent->fts_accpath;
+                    if (auto result = readahead_file(filename, readfully); !result) {
+                        LOG(WARNING)
+                            << "Unable to readahead '" << filename << "': " << result.error();
+                    }
+                }
+            }
+        }
+        LOG(INFO) << "Readahead " << args[1] << " took " << t << " asynchronously";
+        _exit(0);
+    } else if (pid < 0) {
+        return ErrnoError() << "Fork failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_copy(const BuiltinArguments& args) {
+    auto file_contents = ReadFile(args[1]);
+    if (!file_contents) {
+        return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
+    }
+    if (auto result = WriteFile(args[2], *file_contents); !result) {
+        return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
+    }
+
+    return Success();
+}
+
+static Result<Success> do_chown(const BuiltinArguments& args) {
+    auto uid = DecodeUid(args[1]);
+    if (!uid) {
+        return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
     }
 
     // GID is optional and pushes the index of path out by one if specified.
     const std::string& path = (args.size() == 4) ? args[3] : args[2];
-    gid_t gid = -1;
+    Result<gid_t> gid = -1;
 
     if (args.size() == 4) {
-        if (!DecodeUid(args[2], &gid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find GID for '" << args[2] << "': " << decode_uid_err;
-            return -1;
+        gid = DecodeUid(args[2]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
         }
     }
 
-    if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+    if (lchown(path.c_str(), *uid, *gid) == -1) {
+        return ErrnoError() << "lchown() failed";
+    }
 
-    return 0;
+    return Success();
 }
 
 static mode_t get_mode(const char *s) {
@@ -737,15 +841,15 @@
     return mode;
 }
 
-static int do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const BuiltinArguments& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
-        return -errno;
+        return ErrnoError() << "fchmodat() failed";
     }
-    return 0;
+    return Success();
 }
 
-static int do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const BuiltinArguments& args) {
     int ret = 0;
 
     struct flag_type {const char* name; int value;};
@@ -762,8 +866,7 @@
     for (size_t i = 1; i < args.size(); ++i) {
         if (android::base::StartsWith(args[i], "--")) {
             if (!in_flags) {
-                LOG(ERROR) << "restorecon - flags must precede paths";
-                return -1;
+                return Error() << "flags must precede paths";
             }
             bool found = false;
             for (size_t j = 0; flags[j].name; ++j) {
@@ -774,26 +877,27 @@
                 }
             }
             if (!found) {
-                LOG(ERROR) << "restorecon - bad flag " << args[i];
-                return -1;
+                return Error() << "bad flag " << args[i];
             }
         } else {
             in_flags = false;
             if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
-                ret = -errno;
+                ret = errno;
             }
         }
     }
-    return ret;
+
+    if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+    return Success();
 }
 
-static int do_restorecon_recursive(const std::vector<std::string>& args) {
-    std::vector<std::string> non_const_args(args);
+static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+    std::vector<std::string> non_const_args(args.args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
-    return do_restorecon(non_const_args);
+    return do_restorecon({std::move(non_const_args), args.context});
 }
 
-static int do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const BuiltinArguments& args) {
     // TODO: support names instead/as well?
     int log_level = -1;
     android::base::ParseInt(args[1], &log_level);
@@ -808,141 +912,133 @@
         case 1:
         case 0: severity = android::base::FATAL; break;
         default:
-            LOG(ERROR) << "loglevel: invalid log level " << log_level;
-            return -EINVAL;
+            return Error() << "invalid log level " << log_level;
     }
     android::base::SetMinimumLogSeverity(severity);
-    return 0;
+    return Success();
 }
 
-static int do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
     load_persist_props();
-    return 0;
+    return Success();
 }
 
-static int do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const BuiltinArguments& args) {
     load_system_props();
-    return 0;
+    return Success();
 }
 
-static int do_wait(const std::vector<std::string>& args) {
-    if (args.size() == 2) {
-        return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
-    } else if (args.size() == 3) {
-        int timeout;
-        if (android::base::ParseInt(args[2], &timeout)) {
-            return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
+static Result<Success> do_wait(const BuiltinArguments& args) {
+    auto timeout = kCommandRetryTimeout;
+    if (args.size() == 3) {
+        int timeout_int;
+        if (!android::base::ParseInt(args[2], &timeout_int)) {
+            return Error() << "failed to parse timeout";
         }
+        timeout = std::chrono::seconds(timeout_int);
     }
-    return -1;
+
+    if (wait_for_file(args[1].c_str(), timeout) != 0) {
+        return Error() << "wait_for_file() failed";
+    }
+
+    return Success();
 }
 
-static int do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
 
     if (!is_legal_property_name(name)) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: bad name";
-        return -1;
+        return Error() << "is_legal_property_name(" << name << ") failed";
     }
     if (value_len >= PROP_VALUE_MAX) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: value too long";
-        return -1;
+        return Error() << "value too long";
     }
     if (!start_waiting_for_property(name, value)) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: init already in waiting";
-        return -1;
+        return Error() << "already waiting for a property";
     }
-    return 0;
-}
-
-/*
- * Callback to make a directory from the ext4 code
- */
-static int do_installkeys_ensure_dir_exists(const char* dir) {
-    if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
-        return -1;
-    }
-
-    return 0;
+    return Success();
 }
 
 static bool is_file_crypto() {
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static int do_installkey(const std::vector<std::string>& args) {
-    if (!is_file_crypto()) {
-        return 0;
-    }
+static Result<Success> do_installkey(const BuiltinArguments& args) {
+    if (!is_file_crypto()) return Success();
+
     auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
-    if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
-        PLOG(ERROR) << "Failed to create " << unencrypted_dir;
-        return -1;
+    if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
+        return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "enablefilecrypto"};
-    return do_exec(exec_args);
+    return do_exec({std::move(exec_args), args.context});
 }
 
-static int do_init_user0(const std::vector<std::string>& args) {
+static Result<Success> do_init_user0(const BuiltinArguments& args) {
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "init_user0"};
-    return do_exec(exec_args);
+    return do_exec({std::move(exec_args), args.context});
 }
 
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map builtin_functions = {
-        {"bootchart",               {1,     1,    do_bootchart}},
-        {"chmod",                   {2,     2,    do_chmod}},
-        {"chown",                   {2,     3,    do_chown}},
-        {"class_reset",             {1,     1,    do_class_reset}},
-        {"class_restart",           {1,     1,    do_class_restart}},
-        {"class_start",             {1,     1,    do_class_start}},
-        {"class_stop",              {1,     1,    do_class_stop}},
-        {"copy",                    {2,     2,    do_copy}},
-        {"domainname",              {1,     1,    do_domainname}},
-        {"enable",                  {1,     1,    do_enable}},
-        {"exec",                    {1,     kMax, do_exec}},
-        {"exec_start",              {1,     1,    do_exec_start}},
-        {"export",                  {2,     2,    do_export}},
-        {"hostname",                {1,     1,    do_hostname}},
-        {"ifup",                    {1,     1,    do_ifup}},
-        {"init_user0",              {0,     0,    do_init_user0}},
-        {"insmod",                  {1,     kMax, do_insmod}},
-        {"installkey",              {1,     1,    do_installkey}},
-        {"load_persist_props",      {0,     0,    do_load_persist_props}},
-        {"load_system_props",       {0,     0,    do_load_system_props}},
-        {"loglevel",                {1,     1,    do_loglevel}},
-        {"mkdir",                   {1,     4,    do_mkdir}},
-        {"mount_all",               {1,     kMax, do_mount_all}},
-        {"mount",                   {3,     kMax, do_mount}},
-        {"umount",                  {1,     1,    do_umount}},
-        {"restart",                 {1,     1,    do_restart}},
-        {"restorecon",              {1,     kMax, do_restorecon}},
-        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
-        {"rm",                      {1,     1,    do_rm}},
-        {"rmdir",                   {1,     1,    do_rmdir}},
-        {"setprop",                 {2,     2,    do_setprop}},
-        {"setrlimit",               {3,     3,    do_setrlimit}},
-        {"start",                   {1,     1,    do_start}},
-        {"stop",                    {1,     1,    do_stop}},
-        {"swapon_all",              {1,     1,    do_swapon_all}},
-        {"symlink",                 {2,     2,    do_symlink}},
-        {"sysclktz",                {1,     1,    do_sysclktz}},
-        {"trigger",                 {1,     1,    do_trigger}},
-        {"verity_load_state",       {0,     0,    do_verity_load_state}},
-        {"verity_update_state",     {0,     0,    do_verity_update_state}},
-        {"wait",                    {1,     2,    do_wait}},
-        {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
-        {"write",                   {2,     2,    do_write}},
+        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
+        {"chmod",                   {2,     2,    {true,   do_chmod}}},
+        {"chown",                   {2,     3,    {true,   do_chown}}},
+        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
+        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
+        {"class_start",             {1,     1,    {false,  do_class_start}}},
+        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
+        {"copy",                    {2,     2,    {true,   do_copy}}},
+        {"domainname",              {1,     1,    {true,   do_domainname}}},
+        {"enable",                  {1,     1,    {false,  do_enable}}},
+        {"exec",                    {1,     kMax, {false,  do_exec}}},
+        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
+        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
+        {"export",                  {2,     2,    {false,  do_export}}},
+        {"hostname",                {1,     1,    {true,   do_hostname}}},
+        {"ifup",                    {1,     1,    {true,   do_ifup}}},
+        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
+        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
+        {"installkey",              {1,     1,    {false,  do_installkey}}},
+        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
+        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
+        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
+        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
+        {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
+        {"mount",                   {3,     kMax, {false,  do_mount}}},
+        {"umount",                  {1,     1,    {false,  do_umount}}},
+        {"readahead",               {1,     2,    {true,   do_readahead}}},
+        {"restart",                 {1,     1,    {false,  do_restart}}},
+        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
+        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
+        {"rm",                      {1,     1,    {true,   do_rm}}},
+        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
+        //  TODO: setprop should be run in the subcontext, but property service needs to be split
+        //        out from init before that is possible.
+        {"setprop",                 {2,     2,    {false,  do_setprop}}},
+        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
+        {"start",                   {1,     1,    {false,  do_start}}},
+        {"stop",                    {1,     1,    {false,  do_stop}}},
+        {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
+        {"symlink",                 {2,     2,    {true,   do_symlink}}},
+        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
+        {"trigger",                 {1,     1,    {false,  do_trigger}}},
+        {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
+        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
+        {"wait",                    {1,     2,    {true,   do_wait}}},
+        {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
+        {"write",                   {2,     2,    {true,   do_write}}},
     };
     // clang-format on
     return builtin_functions;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/builtins.h b/init/builtins.h
index e1f0567..814b2d5 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -22,10 +22,17 @@
 #include <string>
 #include <vector>
 
+#include "builtin_arguments.h"
 #include "keyword_map.h"
+#include "result.h"
 
-using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
-class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+namespace android {
+namespace init {
+
+using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
+
+using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
+class BuiltinFunctionMap : public KeywordFunctionMap {
   public:
     BuiltinFunctionMap() {}
 
@@ -33,4 +40,7 @@
     const Map& map() const override;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index b8a9ec0..642a364 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -25,6 +25,9 @@
 
 #define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
 
+namespace android {
+namespace init {
+
 static const std::map<std::string, int> cap_map = {
     CAP_MAP_ENTRY(CHOWN),
     CAP_MAP_ENTRY(DAC_OVERRIDE),
@@ -104,17 +107,15 @@
 }
 
 static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
-    cap_t caps = cap_init();
-    auto deleter = [](cap_t* p) { cap_free(*p); };
-    std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter);
+    ScopedCaps caps(cap_init());
 
-    cap_clear(caps);
+    cap_clear(caps.get());
     cap_value_t value[1];
     for (size_t cap = 0; cap < to_keep.size(); ++cap) {
         if (to_keep.test(cap)) {
             value[0] = cap;
-            if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
-                cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
+            if (cap_set_flag(caps.get(), CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
+                cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
                 PLOG(ERROR) << "cap_set_flag(INHERITABLE|PERMITTED, " << cap << ") failed";
                 return false;
             }
@@ -123,14 +124,14 @@
 
     if (add_setpcap) {
         value[0] = CAP_SETPCAP;
-        if (cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
-            cap_set_flag(caps, CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
+        if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
+            cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
             PLOG(ERROR) << "cap_set_flag(PERMITTED|EFFECTIVE, " << CAP_SETPCAP << ") failed";
             return false;
         }
     }
 
-    if (cap_set_proc(caps) != 0) {
+    if (cap_set_proc(caps.get()) != 0) {
         PLOG(ERROR) << "cap_set_proc(" << to_keep.to_ulong() << ") failed";
         return false;
     }
@@ -192,3 +193,6 @@
     // See http://man7.org/linux/man-pages/man7/capabilities.7.html.
     return SetAmbientCaps(to_keep);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/capabilities.h b/init/capabilities.h
index abd7fb2..ede85c3 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -15,16 +15,28 @@
 #ifndef _INIT_CAPABILITIES_H
 #define _INIT_CAPABILITIES_H
 
-#include <linux/capability.h>
+#include <sys/capability.h>
 
 #include <bitset>
 #include <string>
+#include <type_traits>
+
+namespace android {
+namespace init {
+
+struct CapDeleter {
+    void operator()(cap_t caps) const { cap_free(caps); }
+};
 
 using CapSet = std::bitset<CAP_LAST_CAP + 1>;
+using ScopedCaps = std::unique_ptr<std::remove_pointer<cap_t>::type, CapDeleter>;
 
 int LookupCap(const std::string& cap_name);
 bool CapAmbientSupported();
 unsigned int GetLastValidCap();
 bool SetCapsForExec(const CapSet& to_keep);
 
+}  // namespace init
+}  // namespace android
+
 #endif  // _INIT_CAPABILITIES_H
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 6f729a3..6265687 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -28,9 +28,11 @@
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 
-#include "init.h"
 #include "util.h"
 
+namespace android {
+namespace init {
+
 DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
                                gid_t gid, int perm, const std::string& context)
         : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
@@ -58,8 +60,8 @@
   std::for_each(publishedName.begin(), publishedName.end(),
                 [] (char& c) { c = isalnum(c) ? c : '_'; });
 
-  std::string val = android::base::StringPrintf("%d", fd);
-  add_environment(publishedName.c_str(), val.c_str());
+  std::string val = std::to_string(fd);
+  setenv(publishedName.c_str(), val.c_str(), 1);
 
   // make sure we don't close on exec
   fcntl(fd, F_SETFD, 0);
@@ -74,7 +76,8 @@
 }
 
 void SocketInfo::Clean() const {
-  unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str());
+    std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
+    unlink(path.c_str());
 }
 
 int SocketInfo::Create(const std::string& context) const {
@@ -82,8 +85,7 @@
     int flags =
         ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
     bool passcred = types.size() > 1 && types[1] == "passcred";
-    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
-                        sehandle);
+    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
 }
 
 const std::string SocketInfo::key() const {
@@ -125,3 +127,6 @@
 const std::string FileInfo::key() const {
   return ANDROID_FILE_ENV_PREFIX;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h
index ff276fb..3bdddfe 100644
--- a/init/descriptors.h
+++ b/init/descriptors.h
@@ -22,6 +22,9 @@
 
 #include <string>
 
+namespace android {
+namespace init {
+
 class DescriptorInfo {
  public:
   DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
@@ -75,4 +78,7 @@
   virtual const std::string key() const override;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 2943fb7..af6b50a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -30,6 +30,7 @@
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
+#include "selinux.h"
 #include "ueventd.h"
 #include "util.h"
 
@@ -37,13 +38,23 @@
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
 #endif
 
+using android::base::Basename;
+using android::base::Dirname;
+using android::base::Readlink;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+
+namespace android {
+namespace init {
+
 /* Given a path that may start with a PCI device, populate the supplied buffer
  * with the PCI domain/bus number and the peripheral ID and return 0.
  * If it doesn't start with a PCI device, or there is some error, return -1 */
 static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
     result->clear();
 
-    if (!android::base::StartsWith(path, "/devices/pci")) return false;
+    if (!StartsWith(path, "/devices/pci")) return false;
 
     /* Beginning of the prefix is the initial "pci" after "/devices/" */
     std::string::size_type start = 9;
@@ -74,7 +85,7 @@
 static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
     result->clear();
 
-    if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
+    if (!StartsWith(path, "/devices/vbd-")) return false;
 
     /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
     std::string::size_type start = 13;
@@ -116,14 +127,14 @@
 }
 
 bool Permissions::Match(const std::string& path) const {
-    if (prefix_) return android::base::StartsWith(path, name_.c_str());
+    if (prefix_) return StartsWith(path, name_.c_str());
     if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
     return path == name_;
 }
 
 bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
                                           const std::string& subsystem) const {
-    std::string path_basename = android::base::Basename(path);
+    std::string path_basename = Basename(path);
     if (name().find(subsystem) != std::string::npos) {
         if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
         if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
@@ -156,11 +167,11 @@
     // Uevents don't contain the mount point, so we need to add it here.
     path.insert(0, sysfs_mount_point_);
 
-    std::string directory = android::base::Dirname(path);
+    std::string directory = Dirname(path);
 
     while (directory != "/" && directory != ".") {
         std::string subsystem_link_path;
-        if (android::base::Realpath(directory + "/subsystem", &subsystem_link_path) &&
+        if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
             subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
             // We need to remove the mount point that we added above before returning.
             directory.erase(0, sysfs_mount_point_.size());
@@ -172,7 +183,7 @@
         if (last_slash == std::string::npos) return false;
 
         path.erase(last_slash);
-        directory = android::base::Dirname(path);
+        directory = Dirname(path);
     }
 
     return false;
@@ -209,23 +220,18 @@
     return {0600, 0, 0};
 }
 
-void DeviceHandler::MakeDevice(const std::string& path, int block, int major, int minor,
+void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
                                const std::vector<std::string>& links) const {
     auto[mode, uid, gid] = GetDevicePermissions(path, links);
     mode |= (block ? S_IFBLK : S_IFCHR);
 
-    char* secontext = nullptr;
-    if (sehandle_) {
-        std::vector<const char*> c_links;
-        for (const auto& link : links) {
-            c_links.emplace_back(link.c_str());
-        }
-        c_links.emplace_back(nullptr);
-        if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
-            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
-            return;
-        }
-        setfscreatecon(secontext);
+    std::string secontext;
+    if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
+        PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+        return;
+    }
+    if (!secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
     dev_t dev = makedev(major, minor);
@@ -240,7 +246,7 @@
     }
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
+    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
         char* fcon = nullptr;
         int rc = lgetfilecon(path.c_str(), &fcon);
         if (rc < 0) {
@@ -248,10 +254,10 @@
             goto out;
         }
 
-        bool different = strcmp(fcon, secontext) != 0;
+        bool different = fcon != secontext;
         freecon(fcon);
 
-        if (different && lsetfilecon(path.c_str(), secontext)) {
+        if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
             PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
                         << "' device";
         }
@@ -263,51 +269,11 @@
         PLOG(FATAL) << "setegid(AID_ROOT) failed";
     }
 
-    if (secontext) {
-        freecon(secontext);
+    if (!secontext.empty()) {
         setfscreatecon(nullptr);
     }
 }
 
-std::vector<std::string> DeviceHandler::GetCharacterDeviceSymlinks(const Uevent& uevent) const {
-    std::string parent_device;
-    if (!FindPlatformDevice(uevent.path, &parent_device)) return {};
-
-    // skip path to the parent driver
-    std::string path = uevent.path.substr(parent_device.length());
-
-    if (!android::base::StartsWith(path, "/usb")) return {};
-
-    // skip root hub name and device. use device interface
-    // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
-    // then extract what comes between the 3rd and 4th slash
-    // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
-
-    std::string::size_type start = 0;
-    start = path.find('/', start + 1);
-    if (start == std::string::npos) return {};
-
-    start = path.find('/', start + 1);
-    if (start == std::string::npos) return {};
-
-    auto end = path.find('/', start + 1);
-    if (end == std::string::npos) return {};
-
-    start++;  // Skip the first '/'
-
-    auto length = end - start;
-    if (length == 0) return {};
-
-    auto name_string = path.substr(start, length);
-
-    std::vector<std::string> links;
-    links.emplace_back("/dev/usb/" + uevent.subsystem + name_string);
-
-    mkdir("/dev/usb", 0755);
-
-    return links;
-}
-
 // replaces any unacceptable characters with '_', the
 // length of the resulting string is equal to the input string
 void SanitizePartitionName(std::string* string) {
@@ -334,9 +300,9 @@
         static const std::string devices_platform_prefix = "/devices/platform/";
         static const std::string devices_prefix = "/devices/";
 
-        if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
+        if (StartsWith(device, devices_platform_prefix.c_str())) {
             device = device.substr(devices_platform_prefix.length());
-        } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
+        } else if (StartsWith(device, devices_prefix.c_str())) {
             device = device.substr(devices_prefix.length());
         }
 
@@ -375,13 +341,13 @@
     return links;
 }
 
-void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, int block,
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
                                  int major, int minor, const std::vector<std::string>& links) const {
     if (action == "add") {
         MakeDevice(devpath, block, major, minor, links);
         for (const auto& link : links) {
-            if (mkdir_recursive(android::base::Dirname(link), 0755, sehandle_)) {
-                PLOG(ERROR) << "Failed to create directory " << android::base::Dirname(link);
+            if (!mkdir_recursive(Dirname(link), 0755)) {
+                PLOG(ERROR) << "Failed to create directory " << Dirname(link);
             }
 
             if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
@@ -393,7 +359,7 @@
     if (action == "remove") {
         for (const auto& link : links) {
             std::string link_path;
-            if (android::base::Readlink(link, &link_path) && link_path == devpath) {
+            if (Readlink(link, &link_path) && link_path == devpath) {
                 unlink(link.c_str());
             }
         }
@@ -401,71 +367,50 @@
     }
 }
 
-void DeviceHandler::HandleBlockDeviceEvent(const Uevent& uevent) const {
-    // if it's not a /dev device, nothing to do
-    if (uevent.major < 0 || uevent.minor < 0) return;
-
-    const char* base = "/dev/block/";
-    make_dir(base, 0755, sehandle_);
-
-    std::string name = android::base::Basename(uevent.path);
-    std::string devpath = base + name;
-
-    std::vector<std::string> links;
-    if (android::base::StartsWith(uevent.path, "/devices")) {
-        links = GetBlockDeviceSymlinks(uevent);
-    }
-
-    HandleDevice(uevent.action, devpath, 1, uevent.major, uevent.minor, links);
-}
-
-void DeviceHandler::HandleGenericDeviceEvent(const Uevent& uevent) const {
-    // if it's not a /dev device, nothing to do
-    if (uevent.major < 0 || uevent.minor < 0) return;
-
-    std::string devpath;
-
-    if (android::base::StartsWith(uevent.subsystem, "usb")) {
-        if (uevent.subsystem == "usb") {
-            if (!uevent.device_name.empty()) {
-                devpath = "/dev/" + uevent.device_name;
-            } else {
-                // This imitates the file system that would be created
-                // if we were using devfs instead.
-                // Minors are broken up into groups of 128, starting at "001"
-                int bus_id = uevent.minor / 128 + 1;
-                int device_id = uevent.minor % 128 + 1;
-                devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
-            }
-        } else {
-            // ignore other USB events
-            return;
-        }
-    } else if (const auto subsystem =
-                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
-               subsystem != subsystems_.cend()) {
-        devpath = subsystem->ParseDevPath(uevent);
-    } else {
-        devpath = "/dev/" + android::base::Basename(uevent.path);
-    }
-
-    mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle_);
-
-    auto links = GetCharacterDeviceSymlinks(uevent);
-
-    HandleDevice(uevent.action, devpath, 0, uevent.major, uevent.minor, links);
-}
-
 void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
         FixupSysPermissions(uevent.path, uevent.subsystem);
     }
 
+    // if it's not a /dev device, nothing to do
+    if (uevent.major < 0 || uevent.minor < 0) return;
+
+    std::string devpath;
+    std::vector<std::string> links;
+    bool block = false;
+
     if (uevent.subsystem == "block") {
-        HandleBlockDeviceEvent(uevent);
+        block = true;
+        devpath = "/dev/block/" + Basename(uevent.path);
+
+        if (StartsWith(uevent.path, "/devices")) {
+            links = GetBlockDeviceSymlinks(uevent);
+        }
+    } else if (const auto subsystem =
+                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
+               subsystem != subsystems_.cend()) {
+        devpath = subsystem->ParseDevPath(uevent);
+    } else if (uevent.subsystem == "usb") {
+        if (!uevent.device_name.empty()) {
+            devpath = "/dev/" + uevent.device_name;
+        } else {
+            // This imitates the file system that would be created
+            // if we were using devfs instead.
+            // Minors are broken up into groups of 128, starting at "001"
+            int bus_id = uevent.minor / 128 + 1;
+            int device_id = uevent.minor % 128 + 1;
+            devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+        }
+    } else if (StartsWith(uevent.subsystem, "usb")) {
+        // ignore other USB events
+        return;
     } else {
-        HandleGenericDeviceEvent(uevent);
+        devpath = "/dev/" + Basename(uevent.path);
     }
+
+    mkdir_recursive(Dirname(devpath), 0755);
+
+    HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
 }
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
@@ -474,10 +419,12 @@
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
       subsystems_(std::move(subsystems)),
-      sehandle_(selinux_android_file_context_handle()),
       skip_restorecon_(skip_restorecon),
       sysfs_mount_point_("/sys") {}
 
 DeviceHandler::DeviceHandler()
     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
                     std::vector<Subsystem>{}, false) {}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/devices.h b/init/devices.h
index 362c38c..1f8f1e8 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -29,6 +29,9 @@
 
 #include "uevent.h"
 
+namespace android {
+namespace init {
+
 class Permissions {
   public:
     Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
@@ -69,6 +72,7 @@
     friend class SubsystemParser;
 
     Subsystem() {}
+    Subsystem(std::string name) : name_(std::move(name)) {}
 
     // Returns the full path for a uevent of a device that is a member of this subsystem,
     // according to the rules parsed from ueventd.rc
@@ -112,20 +116,15 @@
     bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
     std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
         const std::string& path, const std::vector<std::string>& links) const;
-    void MakeDevice(const std::string& path, int block, int major, int minor,
+    void MakeDevice(const std::string& path, bool block, int major, int minor,
                     const std::vector<std::string>& links) const;
-    std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
-    void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
+    void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,
                       int minor, const std::vector<std::string>& links) const;
     void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
 
-    void HandleBlockDeviceEvent(const Uevent& uevent) const;
-    void HandleGenericDeviceEvent(const Uevent& uevent) const;
-
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
     std::vector<Subsystem> subsystems_;
-    selabel_handle* sehandle_;
     bool skip_restorecon_;
     std::string sysfs_mount_point_;
 };
@@ -133,4 +132,7 @@
 // Exposed for testing
 void SanitizePartitionName(std::string* string);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index e1e4e49..eba00cb 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -24,28 +24,27 @@
 
 using namespace std::string_literals;
 
+namespace android {
+namespace init {
+
 class DeviceHandlerTester {
   public:
     void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
-                         const std::vector<std::string> expected_links, bool block) {
+                         const std::vector<std::string> expected_links) {
         TemporaryDir fake_sys_root;
         device_handler_.sysfs_mount_point_ = fake_sys_root.path;
 
         std::string platform_device_dir = fake_sys_root.path + platform_device;
-        mkdir_recursive(platform_device_dir, 0777, nullptr);
+        mkdir_recursive(platform_device_dir, 0777);
 
         std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
-        mkdir_recursive(platform_bus, 0777, nullptr);
+        mkdir_recursive(platform_bus, 0777);
         symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
 
-        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);
 
         std::vector<std::string> result;
-        if (block) {
-            result = device_handler_.GetBlockDeviceSymlinks(uevent);
-        } else {
-            result = device_handler_.GetCharacterDeviceSymlinks(uevent);
-        }
+        result = device_handler_.GetBlockDeviceSymlinks(uevent);
 
         auto expected_size = expected_links.size();
         ASSERT_EQ(expected_size, result.size());
@@ -61,95 +60,6 @@
     DeviceHandler device_handler_;
 };
 
-TEST(device_handler, get_character_device_symlinks_success) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
-        .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result{"/dev/usb/ttyname"};
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_pdev_match) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_nothing_after_platform_device) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/devices/platform/some_device_name", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_usb_found) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_roothub) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_usb_device) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_final_slash) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_final_name) {
-    const char* platform_device = "/devices/platform/some_device_name";
-    Uevent uevent = {
-        .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
-    };
-    std::vector<std::string> expected_result;
-
-    DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
 TEST(device_handler, get_block_device_symlinks_success_platform) {
     // These are actual paths from bullhead
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
@@ -161,7 +71,7 @@
     std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
@@ -179,7 +89,7 @@
     };
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
@@ -195,7 +105,7 @@
     };
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
@@ -211,7 +121,7 @@
     };
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_success_pci) {
@@ -222,7 +132,7 @@
     std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
@@ -233,7 +143,7 @@
     std::vector<std::string> expected_result{};
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_success_vbd) {
@@ -244,7 +154,7 @@
     std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
@@ -255,7 +165,7 @@
     std::vector<std::string> expected_result{};
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, get_block_device_symlinks_no_matches) {
@@ -268,7 +178,7 @@
     std::vector<std::string> expected_result;
 
     DeviceHandlerTester device_handler_tester_;
-    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
 }
 
 TEST(device_handler, sanitize_null) {
@@ -400,3 +310,6 @@
     EXPECT_EQ(0U, permissions.uid());
     EXPECT_EQ(1001U, permissions.gid());
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 844c605..b686885 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -24,16 +24,22 @@
 #include <string>
 #include <thread>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
-#include "util.h"
+using android::base::Timer;
+using android::base::unique_fd;
+using android::base::WriteFully;
+
+namespace android {
+namespace init {
 
 static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
                          int loading_fd, int data_fd) {
     // Start transfer.
-    android::base::WriteFully(loading_fd, "1", 1);
+    WriteFully(loading_fd, "1", 1);
 
     // Copy the firmware.
     int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
@@ -44,7 +50,7 @@
 
     // Tell the firmware whether to abort or commit.
     const char* response = (rc != -1) ? "0" : "-1";
-    android::base::WriteFully(loading_fd, response, strlen(response));
+    WriteFully(loading_fd, response, strlen(response));
 }
 
 static bool IsBooting() {
@@ -60,13 +66,13 @@
     std::string loading = root + "/loading";
     std::string data = root + "/data";
 
-    android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
+    unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
     if (loading_fd == -1) {
         PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
         return;
     }
 
-    android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
+    unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
     if (data_fd == -1) {
         PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
         return;
@@ -78,7 +84,7 @@
 try_loading_again:
     for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
         std::string file = firmware_dirs[i] + uevent.firmware;
-        android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+        unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
         struct stat sb;
         if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
             LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
@@ -104,29 +110,17 @@
     if (uevent.subsystem != "firmware" || uevent.action != "add") return;
 
     // Loading the firmware in a child means we can do that in parallel...
-    // We double fork instead of waiting for these processes.
-    pid_t pid = fork();
+    auto pid = fork();
     if (pid == -1) {
         PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
-        return;
     }
-
     if (pid == 0) {
-        pid = fork();
-        if (pid == -1) {
-            PLOG(ERROR) << "could not fork a sceond time to process firmware event for "
-                        << uevent.firmware;
-            _exit(EXIT_FAILURE);
-        }
-        if (pid == 0) {
-            Timer t;
-            ProcessFirmwareEvent(uevent);
-            LOG(INFO) << "loading " << uevent.path << " took " << t;
-            _exit(EXIT_SUCCESS);
-        }
-
+        Timer t;
+        ProcessFirmwareEvent(uevent);
+        LOG(INFO) << "loading " << uevent.path << " took " << t;
         _exit(EXIT_SUCCESS);
     }
-
-    waitpid(pid, nullptr, 0);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index be9daae..e456ac4 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -19,6 +19,12 @@
 
 #include "uevent.h"
 
+namespace android {
+namespace init {
+
 void HandleFirmwareEvent(const Uevent& uevent);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index 99275e5..e335fd1 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,24 +20,25 @@
 
 #include "util.h"
 
-bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                int line, std::string* err) {
+namespace android {
+namespace init {
+
+Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
     if (args.size() != 2) {
-        *err = "single argument needed for import\n";
-        return false;
+        return Error() << "single argument needed for import\n";
     }
 
     std::string conf_file;
     bool ret = expand_props(args[1], &conf_file);
     if (!ret) {
-        *err = "error while expanding import";
-        return false;
+        return Error() << "error while expanding import";
     }
 
     LOG(INFO) << "Added '" << conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
     imports_.emplace_back(std::move(conf_file), line);
-    return true;
+    return Success();
 }
 
 void ImportParser::EndFile() {
@@ -50,3 +51,6 @@
         }
     }
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/import_parser.h b/init/import_parser.h
index 45cbfad..5a2f894 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -17,16 +17,19 @@
 #ifndef _INIT_IMPORT_PARSER_H
 #define _INIT_IMPORT_PARSER_H
 
-#include "init_parser.h"
-
 #include <string>
 #include <vector>
 
+#include "parser.h"
+
+namespace android {
+namespace init {
+
 class ImportParser : public SectionParser {
   public:
     ImportParser(Parser* parser) : parser_(parser) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
     void EndFile() override;
 
   private:
@@ -37,4 +40,7 @@
     std::vector<std::pair<std::string, int>> imports_;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/init.cpp b/init/init.cpp
index 9652efa..51a98a2 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -16,89 +16,111 @@
 
 #include "init.h"
 
-#include <ctype.h>
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
 #include <paths.h>
+#include <seccomp_policy.h>
 #include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/epoll.h>
 #include <sys/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
+#include <sys/signalfd.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <android-base/chrono_utils.h>
 #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 <cutils/android_reboot.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
-#include <selinux/label.h>
-#include <selinux/selinux.h>
 
-#include <fstream>
 #include <memory>
-#include <vector>
+#include <optional>
 
-#include "action.h"
-#include "bootchart.h"
 #include "import_parser.h"
 #include "init_first_stage.h"
-#include "init_parser.h"
 #include "keychords.h"
 #include "log.h"
 #include "property_service.h"
 #include "reboot.h"
-#include "service.h"
-#include "signal_handler.h"
+#include "security.h"
+#include "selinux.h"
+#include "sigchld_handler.h"
 #include "ueventd.h"
 #include "util.h"
 #include "watchdogd.h"
 
+using namespace std::string_literals;
+
 using android::base::boot_clock;
 using android::base::GetProperty;
-using android::base::StringPrintf;
+using android::base::Timer;
 
-struct selabel_handle *sehandle;
-struct selabel_handle *sehandle_prop;
+namespace android {
+namespace init {
 
 static int property_triggers_enabled = 0;
 
 static char qemu[32];
 
 std::string default_console = "/dev/console";
-static time_t process_needs_restart_at;
-
-const char *ENV[32];
 
 static int epoll_fd = -1;
+static int sigterm_signal_fd = -1;
 
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
 static std::string wait_prop_name;
 static std::string wait_prop_value;
 static bool shutting_down;
+static std::string shutdown_command;
+static bool do_shutdown = false;
+
+std::vector<std::string> late_import_paths;
+
+static std::vector<Subcontext>* subcontexts;
 
 void DumpState() {
-    ServiceManager::GetInstance().DumpState();
+    ServiceList::GetInstance().DumpState();
     ActionManager::GetInstance().DumpState();
 }
 
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
+    Parser parser;
+
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+    return parser;
+}
+
+static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
+    Parser parser = CreateParser(action_manager, service_list);
+
+    std::string bootscript = GetProperty("ro.boot.init_rc", "");
+    if (bootscript.empty()) {
+        parser.ParseConfig("/init.rc");
+        if (!parser.ParseConfig("/system/etc/init")) {
+            late_import_paths.emplace_back("/system/etc/init");
+        }
+        if (!parser.ParseConfig("/vendor/etc/init")) {
+            late_import_paths.emplace_back("/vendor/etc/init");
+        }
+        if (!parser.ParseConfig("/odm/etc/init")) {
+            late_import_paths.emplace_back("/odm/etc/init");
+        }
+    } else {
+        parser.ParseConfig(bootscript);
+    }
+}
+
 void register_epoll_handler(int fd, void (*fn)()) {
     epoll_event ev;
     ev.events = EPOLLIN;
@@ -108,38 +130,6 @@
     }
 }
 
-/* add_environment - add "key=value" to the current environment */
-int add_environment(const char *key, const char *val)
-{
-    size_t n;
-    size_t key_len = strlen(key);
-
-    /* The last environment entry is reserved to terminate the list */
-    for (n = 0; n < (arraysize(ENV) - 1); n++) {
-
-        /* Delete any existing entry for this key */
-        if (ENV[n] != NULL) {
-            size_t entry_key_len = strcspn(ENV[n], "=");
-            if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
-                free((char*)ENV[n]);
-                ENV[n] = NULL;
-            }
-        }
-
-        /* Add entry if a free slot is available */
-        if (ENV[n] == NULL) {
-            char* entry;
-            asprintf(&entry, "%s=%s", key, val);
-            ENV[n] = entry;
-            return 0;
-        }
-    }
-
-    LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
-
-    return -1;
-}
-
 bool start_waiting_for_property(const char *name, const char *value)
 {
     if (waiting_for_prop) {
@@ -171,9 +161,16 @@
     // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
     // commands to be executed.
     if (name == "sys.powerctl") {
-        if (HandlePowerctlMessage(value)) {
-            shutting_down = true;
-        }
+        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
+        // because it modifies the contents of the action queue, which can cause the action queue
+        // to get into a bad state if this function is called from a command being executed by the
+        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
+        // command is run in the main init loop.
+        // TODO: once property service is removed from init, this will never happen from a builtin,
+        // but rather from a callback from the property service socket, in which case this hack can
+        // go away.
+        shutdown_command = value;
+        do_shutdown = true;
     }
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
@@ -186,23 +183,36 @@
     }
 }
 
-static void restart_processes()
-{
-    process_needs_restart_at = 0;
-    ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
-        s->RestartIfNeeded(&process_needs_restart_at);
-    });
+static std::optional<boot_clock::time_point> RestartProcesses() {
+    std::optional<boot_clock::time_point> next_process_restart_time;
+    for (const auto& s : ServiceList::GetInstance()) {
+        if (!(s->flags() & SVC_RESTARTING)) continue;
+
+        auto restart_time = s->time_started() + 5s;
+        if (boot_clock::now() > restart_time) {
+            if (auto result = s->Start(); !result) {
+                LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
+            }
+        } else {
+            if (!next_process_restart_time || restart_time < *next_process_restart_time) {
+                next_process_restart_time = restart_time;
+            }
+        }
+    }
+    return next_process_restart_time;
 }
 
 void handle_control_message(const std::string& msg, const std::string& name) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
+    Service* svc = ServiceList::GetInstance().FindService(name);
     if (svc == nullptr) {
         LOG(ERROR) << "no such service '" << name << "'";
         return;
     }
 
     if (msg == "start") {
-        svc->Start();
+        if (auto result = svc->Start(); !result) {
+            LOG(ERROR) << "Could not ctl.start service '" << name << "': " << result.error();
+        }
     } else if (msg == "stop") {
         svc->Stop();
     } else if (msg == "restart") {
@@ -212,7 +222,7 @@
     }
 }
 
-static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
     Timer t;
 
     LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -226,233 +236,24 @@
     // because any build that slow isn't likely to boot at all, and we'd
     // rather any test lab devices fail back to the bootloader.
     if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
-        LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
-        panic();
+        LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
     }
 
-    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()).c_str());
-    return 0;
+    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
+    return Success();
 }
 
-/*
- * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
- * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
- * Does nothing if Hardware RNG is not present.
- *
- * Since we don't yet trust the quality of Hardware RNG, these bytes are not
- * mixed into the primary pool of Linux RNG and the entropy estimate is left
- * unmodified.
- *
- * If the HW RNG device /dev/hw_random is present, we require that at least
- * 512 bytes read from it are written into Linux RNG. QA is expected to catch
- * devices/configurations where these I/O operations are blocking for a long
- * time. We do not reboot or halt on failures, as this is a best-effort
- * attempt.
- */
-static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
-{
-    int result = -1;
-    int hwrandom_fd = -1;
-    int urandom_fd = -1;
-    char buf[512];
-    ssize_t chunk_size;
-    size_t total_bytes_written = 0;
-
-    hwrandom_fd = TEMP_FAILURE_RETRY(
-            open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
-    if (hwrandom_fd == -1) {
-        if (errno == ENOENT) {
-            LOG(ERROR) << "/dev/hw_random not found";
-            // It's not an error to not have a Hardware RNG.
-            result = 0;
-        } else {
-            PLOG(ERROR) << "Failed to open /dev/hw_random";
-        }
-        goto ret;
-    }
-
-    urandom_fd = TEMP_FAILURE_RETRY(
-            open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
-    if (urandom_fd == -1) {
-        PLOG(ERROR) << "Failed to open /dev/urandom";
-        goto ret;
-    }
-
-    while (total_bytes_written < sizeof(buf)) {
-        chunk_size = TEMP_FAILURE_RETRY(
-                read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
-        if (chunk_size == -1) {
-            PLOG(ERROR) << "Failed to read from /dev/hw_random";
-            goto ret;
-        } else if (chunk_size == 0) {
-            LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
-            goto ret;
-        }
-
-        chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
-        if (chunk_size == -1) {
-            PLOG(ERROR) << "Failed to write to /dev/urandom";
-            goto ret;
-        }
-        total_bytes_written += chunk_size;
-    }
-
-    LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
-    result = 0;
-
-ret:
-    if (hwrandom_fd != -1) {
-        close(hwrandom_fd);
-    }
-    if (urandom_fd != -1) {
-        close(urandom_fd);
-    }
-    return result;
-}
-
-static void security_failure() {
-    LOG(ERROR) << "Security failure...";
-    panic();
-}
-
-static bool set_highest_available_option_value(std::string path, int min, int max)
-{
-    std::ifstream inf(path, std::fstream::in);
-    if (!inf) {
-        LOG(ERROR) << "Cannot open for reading: " << path;
-        return false;
-    }
-
-    int current = max;
-    while (current >= min) {
-        // try to write out new value
-        std::string str_val = std::to_string(current);
-        std::ofstream of(path, std::fstream::out);
-        if (!of) {
-            LOG(ERROR) << "Cannot open for writing: " << path;
-            return false;
-        }
-        of << str_val << std::endl;
-        of.close();
-
-        // check to make sure it was recorded
-        inf.seekg(0);
-        std::string str_rec;
-        inf >> str_rec;
-        if (str_val.compare(str_rec) == 0) {
-            break;
-        }
-        current--;
-    }
-    inf.close();
-
-    if (current < min) {
-        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
-        return false;
-    }
-    return true;
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
-    std::string path;
-    if (compat) {
-        path = MMAP_RND_COMPAT_PATH;
-    } else {
-        path = MMAP_RND_PATH;
-    }
-
-    return set_highest_available_option_value(path, min, start);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.
- *
- * To support this sysctl, the following upstream commits are needed:
- *
- * d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
- * e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
- * 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
- * 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
- * ec9ee4acd97c drivers: char: random: add get_random_long()
- * 5ef11c35ce86 mm: ASLR: use get_random_long()
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
-{
-    int ret = -1;
-
-    /* values are arch-dependent */
-#if defined(__aarch64__)
-    /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
-    if (set_mmap_rnd_bits_min(33, 24, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__x86_64__)
-    /* x86_64 supports 28 - 32 bits */
-    if (set_mmap_rnd_bits_min(32, 32, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__arm__) || defined(__i386__)
-    /* check to see if we're running on 64-bit kernel */
-    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
-    /* supported 32-bit architecture must have 16 bits set */
-    if (set_mmap_rnd_bits_min(16, 16, h64)) {
-        ret = 0;
-    }
-#elif defined(__mips__) || defined(__mips64__)
-    // TODO: add mips support b/27788820
-    ret = 0;
-#else
-    LOG(ERROR) << "Unknown architecture";
-#endif
-
-    if (ret == -1) {
-        LOG(ERROR) << "Unable to set adequate mmap entropy value!";
-        security_failure();
-    }
-    return ret;
-}
-
-#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
-#define KPTR_RESTRICT_MINVALUE 2
-#define KPTR_RESTRICT_MAXVALUE 4
-
-/* Set kptr_restrict to the highest available level.
- *
- * Aborts if unable to set this to an acceptable value.
- */
-static int set_kptr_restrict_action(const std::vector<std::string>& args)
-{
-    std::string path = KPTR_RESTRICT_PATH;
-
-    if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
-        LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
-        security_failure();
-    }
-    return 0;
-}
-
-static int keychord_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> keychord_init_action(const BuiltinArguments& args) {
     keychord_init();
-    return 0;
+    return Success();
 }
 
-static int console_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> console_init_action(const BuiltinArguments& args) {
     std::string console = GetProperty("ro.boot.console", "");
     if (!console.empty()) {
         default_console = "/dev/" + console;
     }
-    return 0;
+    return Success();
 }
 
 static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -460,14 +261,14 @@
 
     if (for_emulator) {
         // In the emulator, export any kernel option with the "ro.kernel." prefix.
-        property_set(StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
+        property_set("ro.kernel." + key, value);
         return;
     }
 
     if (key == "qemu") {
         strlcpy(qemu, value.c_str(), sizeof(qemu));
     } else if (android::base::StartsWith(key, "androidboot.")) {
-        property_set(StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), value.c_str());
+        property_set("ro.boot." + key.substr(12), value);
     }
 }
 
@@ -498,7 +299,7 @@
     };
     for (size_t i = 0; i < arraysize(prop_map); i++) {
         std::string value = GetProperty(prop_map[i].src_prop, "");
-        property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
+        property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
     }
 }
 
@@ -507,7 +308,7 @@
         return;
     }
 
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
     if (!dir) return;
 
     std::string dt_file;
@@ -517,13 +318,12 @@
             continue;
         }
 
-        std::string file_name = kAndroidDtDir + dp->d_name;
+        std::string file_name = get_android_dt_dir() + dp->d_name;
 
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
 
-        std::string property_name = StringPrintf("ro.boot.%s", dp->d_name);
-        property_set(property_name.c_str(), dt_file.c_str());
+        property_set("ro.boot."s + dp->d_name, dt_file);
     }
 }
 
@@ -535,396 +335,24 @@
     if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static int property_enable_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
     /* Enable property triggers. */
     property_triggers_enabled = 1;
-    return 0;
+    return Success();
 }
 
-static int queue_property_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
     ActionManager::GetInstance().QueueAllPropertyActions();
-    return 0;
+    return Success();
 }
 
-static void selinux_init_all_handles(void)
-{
-    sehandle = selinux_android_file_context_handle();
-    selinux_android_set_sehandle(sehandle);
-    sehandle_prop = selinux_android_prop_context_handle();
-}
-
-enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-
-static selinux_enforcing_status selinux_status_from_cmdline() {
-    selinux_enforcing_status status = SELINUX_ENFORCING;
-
-    import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
-        if (key == "androidboot.selinux" && value == "permissive") {
-            status = SELINUX_PERMISSIVE;
+static void global_seccomp() {
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+            LOG(FATAL) << "Failed to globally enable seccomp!";
         }
     });
-
-    return status;
-}
-
-static bool selinux_is_enforcing(void)
-{
-    if (ALLOW_PERMISSIVE_SELINUX) {
-        return selinux_status_from_cmdline() == SELINUX_ENFORCING;
-    }
-    return true;
-}
-
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-
-    property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
-
-    if (!d || !d->name || !d->cr) {
-        LOG(ERROR) << "audit_callback invoked with null data arguments!";
-        return 0;
-    }
-
-    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
-            d->cr->pid, d->cr->uid, d->cr->gid);
-    return 0;
-}
-
-/*
- * Forks, executes the provided program in the child, and waits for the completion in the parent.
- * Child's stderr is captured and logged using LOG(ERROR).
- *
- * Returns true if the child exited with status code 0, returns false otherwise.
- */
-static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
-                                                char* const envp[]) {
-    // Create a pipe used for redirecting child process's output.
-    // * pipe_fds[0] is the FD the parent will use for reading.
-    // * pipe_fds[1] is the FD the child will use for writing.
-    int pipe_fds[2];
-    if (pipe(pipe_fds) == -1) {
-        PLOG(ERROR) << "Failed to create pipe";
-        return false;
-    }
-
-    pid_t child_pid = fork();
-    if (child_pid == -1) {
-        PLOG(ERROR) << "Failed to fork for " << filename;
-        return false;
-    }
-
-    if (child_pid == 0) {
-        // fork succeeded -- this is executing in the child process
-
-        // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[0]));
-
-        // Redirect stderr to the pipe FD provided by the parent
-        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
-            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
-            _exit(127);
-            return false;
-        }
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
-        if (execve(filename, argv, envp) == -1) {
-            PLOG(ERROR) << "Failed to execve " << filename;
-            return false;
-        }
-        // Unreachable because execve will have succeeded and replaced this code
-        // with child process's code.
-        _exit(127);
-        return false;
-    } else {
-        // fork succeeded -- this is executing in the original/parent process
-
-        // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
-        // Log the redirected output of the child process.
-        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
-        // As a result, we're buffering all output and logging it in one go at the end of the
-        // invocation, instead of logging it as it comes in.
-        const int child_out_fd = pipe_fds[0];
-        std::string child_output;
-        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
-            PLOG(ERROR) << "Failed to capture full output of " << filename;
-        }
-        TEMP_FAILURE_RETRY(close(child_out_fd));
-        if (!child_output.empty()) {
-            // Log captured output, line by line, because LOG expects to be invoked for each line
-            std::istringstream in(child_output);
-            std::string line;
-            while (std::getline(in, line)) {
-                LOG(ERROR) << filename << ": " << line;
-            }
-        }
-
-        // Wait for child to terminate
-        int status;
-        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
-            PLOG(ERROR) << "Failed to wait for " << filename;
-            return false;
-        }
-
-        if (WIFEXITED(status)) {
-            int status_code = WEXITSTATUS(status);
-            if (status_code == 0) {
-                return true;
-            } else {
-                LOG(ERROR) << filename << " exited with status " << status_code;
-            }
-        } else if (WIFSIGNALED(status)) {
-            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
-        } else if (WIFSTOPPED(status)) {
-            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
-        } else {
-            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
-        }
-
-        return false;
-    }
-}
-
-static bool read_first_line(const char* file, std::string* line) {
-    line->clear();
-
-    std::string contents;
-    if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
-        return false;
-    }
-    std::istringstream in(contents);
-    std::getline(in, *line);
-    return true;
-}
-
-static bool selinux_find_precompiled_split_policy(std::string* file) {
-    file->clear();
-
-    static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
-    if (access(precompiled_sepolicy, R_OK) == -1) {
-        return false;
-    }
-    std::string actual_plat_id;
-    if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
-                         &actual_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
-        return false;
-    }
-    std::string precompiled_plat_id;
-    if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
-                         &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/vendor/etc/selinux/"
-                      "precompiled_sepolicy.plat_and_mapping.sha256";
-        return false;
-    }
-    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
-        return false;
-    }
-
-    *file = precompiled_sepolicy;
-    return true;
-}
-
-static bool selinux_get_vendor_mapping_version(std::string* plat_vers) {
-    if (!read_first_line("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
-        PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
-        return false;
-    }
-    if (plat_vers->empty()) {
-        LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
-        return false;
-    }
-    return true;
-}
-
-static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-
-static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
-
-/*
- * Loads SELinux policy split across platform/system and non-platform/vendor files.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_split_policy() {
-    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
-    // * platform -- policy needed due to logic contained in the system image,
-    // * non-platform -- policy needed due to logic contained in the vendor image,
-    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
-    //   with newer versions of platform policy.
-    //
-    // secilc is invoked to compile the above three policy files into a single monolithic policy
-    // file. This file is then loaded into the kernel.
-
-    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
-    // must match the platform policy on the system image.
-    std::string precompiled_sepolicy_file;
-    if (selinux_find_precompiled_split_policy(&precompiled_sepolicy_file)) {
-        android::base::unique_fd fd(
-            open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
-        if (fd != -1) {
-            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
-                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
-                return false;
-            }
-            return true;
-        }
-    }
-    // No suitable precompiled policy could be loaded
-
-    LOG(INFO) << "Compiling SELinux policy";
-
-    // Determine the highest policy language version supported by the kernel
-    set_selinuxmnt("/sys/fs/selinux");
-    int max_policy_version = security_policyvers();
-    if (max_policy_version == -1) {
-        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
-        return false;
-    }
-
-    // We store the output of the compilation on /dev because this is the most convenient tmpfs
-    // storage mount available this early in the boot sequence.
-    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
-    android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
-    if (compiled_sepolicy_fd < 0) {
-        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
-        return false;
-    }
-
-    // Determine which mapping file to include
-    std::string vend_plat_vers;
-    if (!selinux_get_vendor_mapping_version(&vend_plat_vers)) {
-        return false;
-    }
-    std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
-    // clang-format off
-    const char* compile_args[] = {
-        "/system/bin/secilc",
-        plat_policy_cil_file,
-        "-M", "true", "-G", "-N",
-        // Target the highest policy language version supported by the kernel
-        "-c", std::to_string(max_policy_version).c_str(),
-        mapping_file.c_str(),
-        "/vendor/etc/selinux/nonplat_sepolicy.cil",
-        "-o", compiled_sepolicy,
-        // We don't care about file_contexts output by the compiler
-        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
-        nullptr};
-    // clang-format on
-
-    if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
-        unlink(compiled_sepolicy);
-        return false;
-    }
-    unlink(compiled_sepolicy);
-
-    LOG(INFO) << "Loading compiled SELinux policy";
-    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
-        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
-        return false;
-    }
-
-    return true;
-}
-
-/*
- * Loads SELinux policy from a monolithic file.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_monolithic_policy() {
-    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
-    if (selinux_android_load_policy() < 0) {
-        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
-        return false;
-    }
-    return true;
-}
-
-/*
- * Loads SELinux policy into the kernel.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_policy() {
-    return selinux_is_split_policy_device() ? selinux_load_split_policy()
-                                            : selinux_load_monolithic_policy();
-}
-
-static void selinux_initialize(bool in_kernel_domain) {
-    Timer t;
-
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
-    cb.func_audit = audit_callback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
-    if (in_kernel_domain) {
-        LOG(INFO) << "Loading SELinux policy";
-        if (!selinux_load_policy()) {
-            panic();
-        }
-
-        bool kernel_enforcing = (security_getenforce() == 1);
-        bool is_enforcing = selinux_is_enforcing();
-        if (kernel_enforcing != is_enforcing) {
-            if (security_setenforce(is_enforcing)) {
-                PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
-                security_failure();
-            }
-        }
-
-        std::string err;
-        if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
-            LOG(ERROR) << err;
-            security_failure();
-        }
-
-        // init's first stage can't set properties, so pass the time to the second stage.
-        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1);
-    } else {
-        selinux_init_all_handles();
-    }
-}
-
-// The files and directories that were created before initial sepolicy load or
-// files on ramdisk need to have their security context restored to the proper
-// value. This must happen before /dev is populated by ueventd.
-static void selinux_restore_context() {
-    LOG(INFO) << "Running restorecon...";
-    selinux_android_restorecon("/dev", 0);
-    selinux_android_restorecon("/dev/kmsg", 0);
-    selinux_android_restorecon("/dev/socket", 0);
-    selinux_android_restorecon("/dev/random", 0);
-    selinux_android_restorecon("/dev/urandom", 0);
-    selinux_android_restorecon("/dev/__properties__", 0);
-
-    selinux_android_restorecon("/file_contexts.bin", 0);
-    selinux_android_restorecon("/plat_file_contexts", 0);
-    selinux_android_restorecon("/nonplat_file_contexts", 0);
-    selinux_android_restorecon("/plat_property_contexts", 0);
-    selinux_android_restorecon("/nonplat_property_contexts", 0);
-    selinux_android_restorecon("/plat_seapp_contexts", 0);
-    selinux_android_restorecon("/nonplat_seapp_contexts", 0);
-    selinux_android_restorecon("/plat_service_contexts", 0);
-    selinux_android_restorecon("/nonplat_service_contexts", 0);
-    selinux_android_restorecon("/plat_hwservice_contexts", 0);
-    selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
-    selinux_android_restorecon("/sepolicy", 0);
-    selinux_android_restorecon("/vndservice_contexts", 0);
-
-    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
-    selinux_android_restorecon("/dev/device-mapper", 0);
-
-    selinux_android_restorecon("/sbin/mke2fs_static", 0);
-    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
 }
 
 // Set the UDC controller for the ConfigFS USB Gadgets.
@@ -943,7 +371,7 @@
     }
 }
 
-static void install_reboot_signal_handlers() {
+static void InstallRebootSignalHandlers() {
     // Instead of panic'ing the kernel as is the default behavior when init crashes,
     // we prefer to reboot to bootloader on development builds, as this will prevent
     // boot looping bad configurations and allow both developers and test farms to easily
@@ -951,9 +379,18 @@
     struct sigaction action;
     memset(&action, 0, sizeof(action));
     sigfillset(&action.sa_mask);
-    action.sa_handler = [](int) {
-        // panic() reboots to bootloader
-        panic();
+    action.sa_handler = [](int signal) {
+        // These signal handlers are also caught for processes forked from init, however we do not
+        // want them to trigger reboot, so we directly call _exit() for children processes here.
+        if (getpid() != 1) {
+            _exit(signal);
+        }
+
+        // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
+        // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
+        // and probably good enough given this is already an error case and only enabled for
+        // development builds.
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
     };
     action.sa_flags = SA_RESTART;
     sigaction(SIGABRT, &action, nullptr);
@@ -968,6 +405,40 @@
     sigaction(SIGTRAP, &action, nullptr);
 }
 
+static void HandleSigtermSignal() {
+    signalfd_siginfo siginfo;
+    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
+    if (bytes_read != sizeof(siginfo)) {
+        PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
+        return;
+    }
+
+    if (siginfo.ssi_pid != 0) {
+        // Drop any userspace SIGTERM requests.
+        LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
+        return;
+    }
+
+    HandlePowerctlMessage("shutdown,container");
+}
+
+static void InstallSigtermHandler() {
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGTERM);
+
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+        PLOG(FATAL) << "failed to block SIGTERM";
+    }
+
+    sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+    if (sigterm_signal_fd == -1) {
+        PLOG(FATAL) << "failed to create signalfd for SIGTERM";
+    }
+
+    register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -977,11 +448,15 @@
         return watchdogd_main(argc, argv);
     }
 
-    if (REBOOT_BOOTLOADER_ON_PANIC) {
-        install_reboot_signal_handlers();
+    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
+        InitKernelLogging(argv);
+        const BuiltinFunctionMap function_map;
+        return SubcontextMain(argc, argv, &function_map);
     }
 
-    add_environment("PATH", _PATH_DEFPATH);
+    if (REBOOT_BOOTLOADER_ON_PANIC) {
+        InstallRebootSignalHandlers();
+    }
 
     bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
 
@@ -991,6 +466,8 @@
         // Clear the umask.
         umask(0);
 
+        clearenv();
+        setenv("PATH", _PATH_DEFPATH, 1);
         // Get the basic filesystem setup we need put together in the initramdisk
         // on / and then we'll let the rc file figure out the rest.
         mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
@@ -1005,7 +482,13 @@
         setgroups(arraysize(groups), groups);
         mount("sysfs", "/sys", "sysfs", 0, NULL);
         mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+
         mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+
+        if constexpr (WORLD_WRITABLE_KMSG) {
+            mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+        }
+
         mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
         mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
 
@@ -1016,27 +499,29 @@
         LOG(INFO) << "init first stage started!";
 
         if (!DoFirstStageMount()) {
-            LOG(ERROR) << "Failed to mount required partitions early ...";
-            panic();
+            LOG(FATAL) << "Failed to mount required partitions early ...";
         }
 
         SetInitAvbVersionInRecovery();
 
+        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+        global_seccomp();
+
         // Set up SELinux, loading the SELinux policy.
-        selinux_initialize(true);
+        SelinuxSetupKernelLogging();
+        SelinuxInitialize();
 
         // We're in the kernel domain, so re-exec init to transition to the init domain now
         // that the SELinux policy has been loaded.
         if (selinux_android_restorecon("/init", 0) == -1) {
-            PLOG(ERROR) << "restorecon failed";
-            security_failure();
+            PLOG(FATAL) << "restorecon failed of /init failed";
         }
 
         setenv("INIT_SECOND_STAGE", "true", 1);
 
         static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
         uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
-        setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1);
+        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
 
         char* path = argv[0];
         char* args[] = { path, nullptr };
@@ -1044,8 +529,7 @@
 
         // execv() only returns if an error happened, in which case we
         // panic and never fall through this conditional.
-        PLOG(ERROR) << "execv(\"" << path << "\") failed";
-        security_failure();
+        PLOG(FATAL) << "execv(\"" << path << "\") failed";
     }
 
     // At this point we're in the second stage of init.
@@ -1086,16 +570,22 @@
     unsetenv("INIT_AVB_VERSION");
 
     // Now set up SELinux for second stage.
-    selinux_initialize(false);
-    selinux_restore_context();
+    SelinuxSetupKernelLogging();
+    SelabelInitialize();
+    SelinuxRestoreContext();
 
     epoll_fd = epoll_create1(EPOLL_CLOEXEC);
     if (epoll_fd == -1) {
-        PLOG(ERROR) << "epoll_create1 failed";
-        exit(1);
+        PLOG(FATAL) << "epoll_create1 failed";
     }
 
-    signal_handler_init();
+    sigchld_handler_init();
+
+    if (!IsRebootCapable()) {
+        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
+        // In that case, receiving SIGTERM will cause the system to shut down.
+        InstallSigtermHandler();
+    }
 
     property_load_boot_defaults();
     export_oem_lock_status();
@@ -1105,27 +595,12 @@
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
-    ActionManager& am = ActionManager::GetInstance();
-    ServiceManager& sm = ServiceManager::GetInstance();
-    Parser& parser = Parser::GetInstance();
+    subcontexts = InitializeSubcontexts();
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
-    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
-    std::string bootscript = GetProperty("ro.boot.init_rc", "");
-    if (bootscript.empty()) {
-        parser.ParseConfig("/init.rc");
-        parser.set_is_system_etc_init_loaded(
-                parser.ParseConfig("/system/etc/init"));
-        parser.set_is_vendor_etc_init_loaded(
-                parser.ParseConfig("/vendor/etc/init"));
-        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
-    } else {
-        parser.ParseConfig(bootscript);
-        parser.set_is_system_etc_init_loaded(true);
-        parser.set_is_vendor_etc_init_loaded(true);
-        parser.set_is_odm_etc_init_loaded(true);
-    }
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceList& sm = ServiceList::GetInstance();
+
+    LoadBootScripts(am, sm);
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
@@ -1136,9 +611,9 @@
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
-    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
-    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
+    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
+    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
@@ -1147,7 +622,7 @@
 
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
-    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
     // Don't mount filesystems or start core system services in charger mode.
     std::string bootmode = GetProperty("ro.bootmode", "");
@@ -1164,16 +639,27 @@
         // By default, sleep until something happens.
         int epoll_timeout_ms = -1;
 
-        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
+        if (do_shutdown && !shutting_down) {
+            do_shutdown = false;
+            if (HandlePowerctlMessage(shutdown_command)) {
+                shutting_down = true;
+            }
+        }
+
+        if (!(waiting_for_prop || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
         }
-        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
-            if (!shutting_down) restart_processes();
+        if (!(waiting_for_prop || Service::is_exec_service_running())) {
+            if (!shutting_down) {
+                auto next_process_restart_time = RestartProcesses();
 
-            // If there's a process that needs restarting, wake up in time for that.
-            if (process_needs_restart_at != 0) {
-                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
-                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+                // If there's a process that needs restarting, wake up in time for that.
+                if (next_process_restart_time) {
+                    epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
+                                           *next_process_restart_time - boot_clock::now())
+                                           .count();
+                    if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+                }
             }
 
             // If there's more work to do, wake up again immediately.
@@ -1191,3 +677,10 @@
 
     return 0;
 }
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::init::main(argc, argv);
+}
diff --git a/init/init.h b/init/init.h
index 4024cfe..b757c1d 100644
--- a/init/init.h
+++ b/init/init.h
@@ -18,14 +18,22 @@
 #define _INIT_INIT_H
 
 #include <string>
+#include <vector>
+
+#include "action.h"
+#include "parser.h"
+#include "service.h"
+
+namespace android {
+namespace init {
 
 // Note: These globals are *only* valid in init, so they should not be used in ueventd,
 // watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
 // TODO: Have an Init class and remove all globals.
-extern const char *ENV[32];
 extern std::string default_console;
-extern struct selabel_handle *sehandle;
-extern struct selabel_handle *sehandle_prop;
+extern std::vector<std::string> late_import_paths;
+
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
 
 void handle_control_message(const std::string& msg, const std::string& arg);
 
@@ -33,12 +41,13 @@
 
 void register_epoll_handler(int fd, void (*fn)());
 
-int add_environment(const char* key, const char* val);
-
 bool start_waiting_for_property(const char *name, const char *value);
 
 void DumpState();
 
 void ResetWaitForProp();
 
+}  // namespace init
+}  // namespace android
+
 #endif  /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index d72c3ac4..0f7e38f 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -25,6 +25,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
@@ -36,7 +37,10 @@
 #include "uevent_listener.h"
 #include "util.h"
 
-using namespace std::chrono_literals;
+using android::base::Timer;
+
+namespace android {
+namespace init {
 
 // Class Declarations
 // ------------------
@@ -508,3 +512,6 @@
     }
     setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/init_first_stage.h b/init/init_first_stage.h
index 170a24c..c7a3867 100644
--- a/init/init_first_stage.h
+++ b/init/init_first_stage.h
@@ -17,7 +17,13 @@
 #ifndef _INIT_FIRST_STAGE_H
 #define _INIT_FIRST_STAGE_H
 
+namespace android {
+namespace init {
+
 bool DoFirstStageMount();
 void SetInitAvbVersionInRecovery();
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
deleted file mode 100644
index 1b31cf2..0000000
--- a/init/init_parser.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2010 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 "init_parser.h"
-
-#include <dirent.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "parser.h"
-#include "util.h"
-
-Parser::Parser() {
-}
-
-Parser& Parser::GetInstance() {
-    static Parser instance;
-    return instance;
-}
-
-void Parser::AddSectionParser(const std::string& name,
-                              std::unique_ptr<SectionParser> parser) {
-    section_parsers_[name] = std::move(parser);
-}
-
-void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
-    line_callbacks_.emplace_back(prefix, callback);
-}
-
-void Parser::ParseData(const std::string& filename, const std::string& data) {
-    //TODO: Use a parser with const input and remove this copy
-    std::vector<char> data_copy(data.begin(), data.end());
-    data_copy.push_back('\0');
-
-    parse_state state;
-    state.line = 0;
-    state.ptr = &data_copy[0];
-    state.nexttoken = 0;
-
-    SectionParser* section_parser = nullptr;
-    std::vector<std::string> args;
-
-    for (;;) {
-        switch (next_token(&state)) {
-        case T_EOF:
-            if (section_parser) {
-                section_parser->EndSection();
-            }
-            return;
-        case T_NEWLINE:
-            state.line++;
-            if (args.empty()) {
-                break;
-            }
-            // If we have a line matching a prefix we recognize, call its callback and unset any
-            // 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 (section_parser) section_parser->EndSection();
-
-                    std::string ret_err;
-                    if (!callback(std::move(args), &ret_err)) {
-                        LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
-                    }
-                    section_parser = nullptr;
-                    break;
-                }
-            }
-            if (section_parsers_.count(args[0])) {
-                if (section_parser) {
-                    section_parser->EndSection();
-                }
-                section_parser = section_parsers_[args[0]].get();
-                std::string ret_err;
-                if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
-                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
-                    section_parser = nullptr;
-                }
-            } else if (section_parser) {
-                std::string ret_err;
-                if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
-                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
-                }
-            }
-            args.clear();
-            break;
-        case T_TEXT:
-            args.emplace_back(state.text);
-            break;
-        }
-    }
-}
-
-bool Parser::ParseConfigFile(const std::string& path) {
-    LOG(INFO) << "Parsing file " << path << "...";
-    Timer t;
-    std::string data;
-    std::string err;
-    if (!ReadFile(path, &data, &err)) {
-        LOG(ERROR) << err;
-        return false;
-    }
-
-    data.push_back('\n'); // TODO: fix parse_config.
-    ParseData(path, data);
-    for (const auto& [section_name, section_parser] : section_parsers_) {
-        section_parser->EndFile();
-    }
-
-    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
-    return true;
-}
-
-bool Parser::ParseConfigDir(const std::string& path) {
-    LOG(INFO) << "Parsing directory " << path << "...";
-    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
-    if (!config_dir) {
-        PLOG(ERROR) << "Could not import directory '" << path << "'";
-        return false;
-    }
-    dirent* current_file;
-    std::vector<std::string> files;
-    while ((current_file = readdir(config_dir.get()))) {
-        // Ignore directories and only process regular files.
-        if (current_file->d_type == DT_REG) {
-            std::string current_path =
-                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
-            files.emplace_back(current_path);
-        }
-    }
-    // Sort first so we load files in a consistent order (bug 31996208)
-    std::sort(files.begin(), files.end());
-    for (const auto& file : files) {
-        if (!ParseConfigFile(file)) {
-            LOG(ERROR) << "could not import file '" << file << "'";
-        }
-    }
-    return true;
-}
-
-bool Parser::ParseConfig(const std::string& path) {
-    if (is_dir(path.c_str())) {
-        return ParseConfigDir(path);
-    }
-    return ParseConfigFile(path);
-}
diff --git a/init/init_parser.h b/init/init_parser.h
deleted file mode 100644
index 722ebb2..0000000
--- a/init/init_parser.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2010 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_INIT_PARSER_H_
-#define _INIT_INIT_PARSER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-//  SectionParser is an interface that can parse a given 'section' in init.
-//
-//  You can implement up to 4 functions below, with ParseSection() being mandatory.
-//  The first two function return bool with false indicating a failure and has a std::string* err
-//  parameter into which an error string can be written.  It will be reported along with the
-//  filename and line number of where the error occurred.
-//
-//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-//                       int line, std::string* err)
-//    This function is called when a section is first encountered.
-//
-//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
-//    This function is called on each subsequent line until the next section is encountered.
-//
-//  3) bool EndSection()
-//    This function is called either when a new section is found or at the end of the file.
-//    It indicates that parsing of the current section is complete and any relevant objects should
-//    be committed.
-//
-//  4) bool EndFile()
-//    This function is called at the end of the file.
-//    It indicates that the parsing has completed and any relevant objects should be committed.
-
-class SectionParser {
-  public:
-    virtual ~SectionParser() {}
-    virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                              int line, std::string* err) = 0;
-    virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
-    virtual void EndSection(){};
-    virtual void EndFile(){};
-};
-
-class Parser {
-  public:
-    //  LineCallback is the type for callbacks that can parse a line starting with a given prefix.
-    //
-    //  They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
-    //
-    //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false
-    //  indicating a failure and has an std::string* err parameter into which an error string can
-    //  be written.
-    using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
-
-    // TODO: init is the only user of this as a singleton; remove it.
-    static Parser& GetInstance();
-
-    Parser();
-
-    bool ParseConfig(const std::string& path);
-    void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
-    void AddSingleLineParser(const std::string& prefix, LineCallback callback);
-    void set_is_system_etc_init_loaded(bool loaded) { is_system_etc_init_loaded_ = loaded; }
-    void set_is_vendor_etc_init_loaded(bool loaded) { is_vendor_etc_init_loaded_ = loaded; }
-    void set_is_odm_etc_init_loaded(bool loaded) { is_odm_etc_init_loaded_ = loaded; }
-    bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
-    bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
-    bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
-
-  private:
-    void ParseData(const std::string& filename, const std::string& data);
-    bool ParseConfigFile(const std::string& path);
-    bool ParseConfigDir(const std::string& path);
-
-    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
-    std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
-    bool is_system_etc_init_loaded_ = false;
-    bool is_vendor_etc_init_loaded_ = false;
-    bool is_odm_etc_init_loaded_ = false;
-};
-
-#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
deleted file mode 100644
index 86d60d0..0000000
--- a/init/init_parser_test.cpp
+++ /dev/null
@@ -1,143 +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 "init_parser.h"
-
-#include "init.h"
-#include "service.h"
-
-#include <gtest/gtest.h>
-
-#include <string>
-#include <vector>
-
-TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
-    ServiceManager& sm = ServiceManager::GetInstance();
-    std::vector<std::string> args;
-    // Nothing.
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
-    // No arguments to 'exec'.
-    args.push_back("exec");
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
-    // No command in "exec --".
-    args.push_back("--");
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
-    ServiceManager& sm = ServiceManager::GetInstance();
-    std::vector<std::string> args;
-    args.push_back("exec");
-    args.push_back("seclabel");
-    args.push_back("root"); // uid.
-    args.push_back("root"); // gid.
-    for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
-        args.push_back("root"); // Supplementary gid.
-    }
-    args.push_back("--");
-    args.push_back("/system/bin/id");
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
-                                           bool gid, bool supplementary_gids) {
-    ServiceManager& sm = ServiceManager::GetInstance();
-    std::vector<std::string> args;
-    args.push_back("exec");
-    if (seclabel) {
-        args.push_back("u:r:su:s0"); // seclabel
-        if (uid) {
-            args.push_back("log");      // uid
-            if (gid) {
-                args.push_back("shell");     // gid
-                if (supplementary_gids) {
-                    args.push_back("system");    // supplementary gid 0
-                    args.push_back("adb");       // supplementary gid 1
-                }
-            }
-        }
-    }
-    if (dash_dash) {
-        args.push_back("--");
-    }
-    args.push_back("/system/bin/toybox");
-    args.push_back("id");
-    Service* svc = sm.MakeExecOneshotService(args);
-    ASSERT_NE(nullptr, svc);
-
-    if (seclabel) {
-        ASSERT_EQ("u:r:su:s0", svc->seclabel());
-    } else {
-        ASSERT_EQ("", svc->seclabel());
-    }
-    if (uid) {
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->uid());
-    } else {
-        ASSERT_EQ(0U, svc->uid());
-    }
-    if (gid) {
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->gid());
-    } else {
-        ASSERT_EQ(0U, svc->gid());
-    }
-    if (supplementary_gids) {
-        ASSERT_EQ(2U, svc->supp_gids().size());
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
-        ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
-    } else {
-        ASSERT_EQ(0U, svc->supp_gids().size());
-    }
-
-    ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
-    ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
-    ASSERT_EQ("id", svc->args()[1]);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_everything) {
-    Test_make_exec_oneshot_service(true, true, true, true, true);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid_gid) {
-    Test_make_exec_oneshot_service(true, true, true, true, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid) {
-    Test_make_exec_oneshot_service(true, true, true, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel) {
-    Test_make_exec_oneshot_service(true, true, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command) {
-    Test_make_exec_oneshot_service(true, false, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
-    Test_make_exec_oneshot_service(false, false, false, false, false);
-}
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 7093ba9..29a65ab 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -23,31 +23,13 @@
 #include "action.h"
 #include "builtins.h"
 #include "import_parser.h"
-#include "init_parser.h"
 #include "keyword_map.h"
+#include "parser.h"
+#include "test_function_map.h"
 #include "util.h"
 
-class TestFunctionMap : public KeywordMap<BuiltinFunction> {
-  public:
-    // Helper for argument-less functions
-    using BuiltinFunctionNoArgs = std::function<void(void)>;
-    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
-        Add(name, 0, 0, [function](const std::vector<std::string>&) {
-            function();
-            return 0;
-        });
-    }
-
-    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
-             const BuiltinFunction function) {
-        builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
-    }
-
-  private:
-    Map builtin_functions_ = {};
-
-    const Map& map() const override { return builtin_functions_; }
-};
+namespace android {
+namespace init {
 
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
@@ -58,7 +40,7 @@
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     ASSERT_TRUE(parser.ParseConfig(init_script_file));
@@ -153,10 +135,9 @@
                                "execute 3";
     // clang-format on
     // WriteFile() ensures the right mode is set
-    std::string err;
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script, &err));
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
 
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5", &err));
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
 
     // clang-format off
     std::string start_script = "import " + std::string(first_import.path) + "\n"
@@ -169,14 +150,14 @@
     ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
 
     int num_executed = 0;
-    auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+    auto execute_command = [&num_executed](const BuiltinArguments& args) {
         EXPECT_EQ(2U, args.size());
         EXPECT_EQ(++num_executed, std::stoi(args[1]));
-        return 0;
+        return Success();
     };
 
     TestFunctionMap test_function_map;
-    test_function_map.Add("execute", 1, 1, execute_command);
+    test_function_map.Add("execute", 1, 1, false, execute_command);
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -185,3 +166,6 @@
 
     EXPECT_EQ(6, num_executed);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keychords.cpp b/init/keychords.cpp
index c572cee..e686ce1 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "keychords.h"
+
 #include <fcntl.h>
 #include <stdlib.h>
 #include <sys/stat.h>
@@ -25,7 +27,9 @@
 #include <android-base/properties.h>
 
 #include "init.h"
-#include "service.h"
+
+namespace android {
+namespace init {
 
 static struct input_keychord *keychords = 0;
 static int keychords_count = 0;
@@ -75,10 +79,13 @@
     // Only handle keychords if adb is enabled.
     std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
     if (adb_enabled == "running") {
-        Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
+        Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
         if (svc) {
-            LOG(INFO) << "Starting service " << svc->name() << " from keychord " << id;
-            svc->Start();
+            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
+            if (auto result = svc->Start(); !result) {
+                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
+                           << ": " << result.error();
+            }
         } else {
             LOG(ERROR) << "Service for keychord " << id << " not found";
         }
@@ -88,7 +95,9 @@
 }
 
 void keychord_init() {
-    ServiceManager::GetInstance().ForEachService(add_service_keycodes);
+    for (const auto& service : ServiceList::GetInstance()) {
+        add_service_keycodes(service.get());
+    }
 
     // Nothing to do if no services require keychords.
     if (!keychords) {
@@ -112,3 +121,6 @@
 
     register_epoll_handler(keychord_fd, handle_keychord);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keychords.h b/init/keychords.h
index d2723b7..1c34098 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,9 +17,15 @@
 #ifndef _INIT_KEYCHORDS_H_
 #define _INIT_KEYCHORDS_H_
 
-struct service;
+#include "service.h"
 
-void add_service_keycodes(service*);
+namespace android {
+namespace init {
+
+void add_service_keycodes(Service* svc);
 void keychord_init();
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 88bad01..c95fc73 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,6 +22,11 @@
 
 #include <android-base/stringprintf.h>
 
+#include "result.h"
+
+namespace android {
+namespace init {
+
 template <typename Function>
 class KeywordMap {
   public:
@@ -31,20 +36,17 @@
     virtual ~KeywordMap() {
     }
 
-    const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
+    const Result<Function> FindFunction(const std::vector<std::string>& args) const {
         using android::base::StringPrintf;
 
-        if (args.empty()) {
-            *err = "keyword needed, but not provided";
-            return nullptr;
-        }
+        if (args.empty()) return Error() << "Keyword needed, but not provided";
+
         auto& keyword = args[0];
         auto num_args = args.size() - 1;
 
         auto function_info_it = map().find(keyword);
         if (function_info_it == map().end()) {
-            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
-            return nullptr;
+            return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
         }
 
         auto function_info = function_info_it->second;
@@ -52,22 +54,18 @@
         auto min_args = std::get<0>(function_info);
         auto max_args = std::get<1>(function_info);
         if (min_args == max_args && num_args != min_args) {
-            *err = StringPrintf("%s requires %zu argument%s",
-                                keyword.c_str(), min_args,
-                                (min_args > 1 || min_args == 0) ? "s" : "");
-            return nullptr;
+            return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
+                                           (min_args > 1 || min_args == 0) ? "s" : "");
         }
 
         if (num_args < min_args || num_args > max_args) {
             if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
-                *err = StringPrintf("%s requires at least %zu argument%s",
-                                    keyword.c_str(), min_args,
-                                    min_args > 1 ? "s" : "");
+                return Error() << StringPrintf("%s requires at least %zu argument%s",
+                                               keyword.c_str(), min_args, min_args > 1 ? "s" : "");
             } else {
-                *err = StringPrintf("%s requires between %zu and %zu arguments",
-                                    keyword.c_str(), min_args, max_args);
+                return Error() << StringPrintf("%s requires between %zu and %zu arguments",
+                                               keyword.c_str(), min_args, max_args);
             }
-            return nullptr;
         }
 
         return std::get<Function>(function_info);
@@ -79,4 +77,7 @@
     virtual const Map& map() const = 0;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/log.cpp b/init/log.cpp
index 0615730..6198fc2 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -19,16 +19,45 @@
 #include <fcntl.h>
 #include <linux/audit.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <android-base/logging.h>
+#include <cutils/android_reboot.h>
 #include <selinux/selinux.h>
 
+#include "reboot.h"
+
+namespace android {
+namespace init {
+
+static void InitAborter(const char* abort_message) {
+    // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
+    // simply abort instead of trying to reboot the system.
+    if (getpid() != 1) {
+        android::base::DefaultAborter(abort_message);
+        return;
+    }
+
+    // DoReboot() does a lot to try to shutdown the system cleanly.  If something happens to call
+    // LOG(FATAL) in the shutdown path, we want to catch this and immediately use the syscall to
+    // reboot instead of recursing here.
+    static bool has_aborted = false;
+    if (!has_aborted) {
+        has_aborted = true;
+        // Do not queue "shutdown" trigger since we want to shutdown immediately and it's not likely
+        // that we can even run the ActionQueue at this point.
+        DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
+    } else {
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+    }
+}
+
 void InitKernelLogging(char* argv[]) {
     // Make stdin/stdout/stderr all point to /dev/null.
     int fd = open("/sys/fs/selinux/null", O_RDWR);
     if (fd == -1) {
         int saved_errno = errno;
-        android::base::InitLogging(argv, &android::base::KernelLogger);
+        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
         errno = saved_errno;
         PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
     }
@@ -37,7 +66,7 @@
     dup2(fd, 2);
     if (fd > 2) close(fd);
 
-    android::base::InitLogging(argv, &android::base::KernelLogger);
+    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
 }
 
 int selinux_klog_callback(int type, const char *fmt, ...) {
@@ -55,3 +84,6 @@
     android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
     return 0;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/log.h b/init/log.h
index 29a27af..5a4eba6 100644
--- a/init/log.h
+++ b/init/log.h
@@ -19,8 +19,14 @@
 
 #include <sys/cdefs.h>
 
+namespace android {
+namespace init {
+
 void InitKernelLogging(char* argv[]);
 
 int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 0d13cfe..8a4e798 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,118 +1,155 @@
+/*
+ * Copyright (C) 2010 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 "parser.h"
 
-int next_token(struct parse_state *state)
-{
-    char *x = state->ptr;
-    char *s;
+#include <dirent.h>
 
-    if (state->nexttoken) {
-        int t = state->nexttoken;
-        state->nexttoken = 0;
-        return t;
-    }
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
-    for (;;) {
-        switch (*x) {
-        case 0:
-            state->ptr = x;
-            return T_EOF;
-        case '\n':
-            x++;
-            state->ptr = x;
-            return T_NEWLINE;
-        case ' ':
-        case '\t':
-        case '\r':
-            x++;
-            continue;
-        case '#':
-            while (*x && (*x != '\n')) x++;
-            if (*x == '\n') {
-                state->ptr = x+1;
-                return T_NEWLINE;
-            } else {
-                state->ptr = x;
-                return T_EOF;
-            }
-        default:
-            goto text;
-        }
-    }
+#include "tokenizer.h"
+#include "util.h"
 
-textdone:
-    state->ptr = x;
-    *s = 0;
-    return T_TEXT;
-text:
-    state->text = s = x;
-textresume:
-    for (;;) {
-        switch (*x) {
-        case 0:
-            goto textdone;
-        case ' ':
-        case '\t':
-        case '\r':
-            x++;
-            goto textdone;
-        case '\n':
-            state->nexttoken = T_NEWLINE;
-            x++;
-            goto textdone;
-        case '"':
-            x++;
-            for (;;) {
-                switch (*x) {
-                case 0:
-                        /* unterminated quoted thing */
-                    state->ptr = x;
-                    return T_EOF;
-                case '"':
-                    x++;
-                    goto textresume;
-                default:
-                    *s++ = *x++;
-                }
-            }
-            break;
-        case '\\':
-            x++;
-            switch (*x) {
-            case 0:
-                goto textdone;
-            case 'n':
-                *s++ = '\n';
-                break;
-            case 'r':
-                *s++ = '\r';
-                break;
-            case 't':
-                *s++ = '\t';
-                break;
-            case '\\':
-                *s++ = '\\';
-                break;
-            case '\r':
-                    /* \ <cr> <lf> -> line continuation */
-                if (x[1] != '\n') {
-                    x++;
-                    continue;
-                }
-            case '\n':
-                    /* \ <lf> -> line continuation */
-                state->line++;
-                x++;
-                    /* eat any extra whitespace */
-                while((*x == ' ') || (*x == '\t')) x++;
-                continue;
-            default:
-                    /* unknown escape -- just copy */
-                *s++ = *x++;
-            }
-            continue;
-        default:
-            *s++ = *x++;
-        }
-    }
-    return T_EOF;
+namespace android {
+namespace init {
+
+Parser::Parser() {}
+
+void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
+    section_parsers_[name] = std::move(parser);
 }
+
+void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
+    line_callbacks_.emplace_back(prefix, callback);
+}
+
+void Parser::ParseData(const std::string& filename, const std::string& data) {
+    // TODO: Use a parser with const input and remove this copy
+    std::vector<char> data_copy(data.begin(), data.end());
+    data_copy.push_back('\0');
+
+    parse_state state;
+    state.line = 0;
+    state.ptr = &data_copy[0];
+    state.nexttoken = 0;
+
+    SectionParser* section_parser = nullptr;
+    std::vector<std::string> args;
+
+    for (;;) {
+        switch (next_token(&state)) {
+            case T_EOF:
+                if (section_parser) section_parser->EndSection();
+                return;
+            case T_NEWLINE:
+                state.line++;
+                if (args.empty()) break;
+                // If we have a line matching a prefix we recognize, call its callback and unset any
+                // 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 (section_parser) section_parser->EndSection();
+
+                        if (auto result = callback(std::move(args)); !result) {
+                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+                        }
+                        section_parser = nullptr;
+                        break;
+                    }
+                }
+                if (section_parsers_.count(args[0])) {
+                    if (section_parser) section_parser->EndSection();
+                    section_parser = section_parsers_[args[0]].get();
+                    if (auto result =
+                            section_parser->ParseSection(std::move(args), filename, state.line);
+                        !result) {
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+                        section_parser = nullptr;
+                    }
+                } else if (section_parser) {
+                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
+                        !result) {
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+                    }
+                }
+                args.clear();
+                break;
+            case T_TEXT:
+                args.emplace_back(state.text);
+                break;
+        }
+    }
+}
+
+bool Parser::ParseConfigFile(const std::string& path) {
+    LOG(INFO) << "Parsing file " << path << "...";
+    android::base::Timer t;
+    auto config_contents = ReadFile(path);
+    if (!config_contents) {
+        LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+        return false;
+    }
+
+    config_contents->push_back('\n');  // TODO: fix parse_config.
+    ParseData(path, *config_contents);
+    for (const auto& [section_name, section_parser] : section_parsers_) {
+        section_parser->EndFile();
+    }
+
+    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
+    return true;
+}
+
+bool Parser::ParseConfigDir(const std::string& path) {
+    LOG(INFO) << "Parsing directory " << path << "...";
+    std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
+    if (!config_dir) {
+        PLOG(ERROR) << "Could not import directory '" << path << "'";
+        return false;
+    }
+    dirent* current_file;
+    std::vector<std::string> files;
+    while ((current_file = readdir(config_dir.get()))) {
+        // Ignore directories and only process regular files.
+        if (current_file->d_type == DT_REG) {
+            std::string current_path =
+                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
+            files.emplace_back(current_path);
+        }
+    }
+    // Sort first so we load files in a consistent order (bug 31996208)
+    std::sort(files.begin(), files.end());
+    for (const auto& file : files) {
+        if (!ParseConfigFile(file)) {
+            LOG(ERROR) << "could not import file '" << file << "'";
+        }
+    }
+    return true;
+}
+
+bool Parser::ParseConfig(const std::string& path) {
+    if (is_dir(path.c_str())) {
+        return ParseConfigDir(path);
+    }
+    return ParseConfigFile(path);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/parser.h b/init/parser.h
index 3dcc566..4ab24a4 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -14,21 +14,79 @@
  * limitations under the License.
  */
 
-#ifndef PARSER_H_
-#define PARSER_H_
+#ifndef _INIT_PARSER_H_
+#define _INIT_PARSER_H_
 
-#define T_EOF 0
-#define T_TEXT 1
-#define T_NEWLINE 2
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
 
-struct parse_state
-{
-    char *ptr;
-    char *text;
-    int line;
-    int nexttoken;
+#include "result.h"
+
+//  SectionParser is an interface that can parse a given 'section' in init.
+//
+//  You can implement up to 4 functions below, with ParseSection() being mandatory.
+//  The first two function return bool with false indicating a failure and has a std::string* err
+//  parameter into which an error string can be written.  It will be reported along with the
+//  filename and line number of where the error occurred.
+//
+//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+//                       int line, std::string* err)
+//    This function is called when a section is first encountered.
+//
+//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+//    This function is called on each subsequent line until the next section is encountered.
+//
+//  3) bool EndSection()
+//    This function is called either when a new section is found or at the end of the file.
+//    It indicates that parsing of the current section is complete and any relevant objects should
+//    be committed.
+//
+//  4) bool EndFile()
+//    This function is called at the end of the file.
+//    It indicates that the parsing has completed and any relevant objects should be committed.
+
+namespace android {
+namespace init {
+
+class SectionParser {
+  public:
+    virtual ~SectionParser() {}
+    virtual Result<Success> ParseSection(std::vector<std::string>&& args,
+                                         const std::string& filename, int line) = 0;
+    virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
+    virtual void EndSection(){};
+    virtual void EndFile(){};
 };
 
-int next_token(struct parse_state *state);
+class Parser {
+  public:
+    //  LineCallback is the type for callbacks that can parse a line starting with a given prefix.
+    //
+    //  They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
+    //
+    //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false
+    //  indicating a failure and has an std::string* err parameter into which an error string can
+    //  be written.
+    using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
 
-#endif /* PARSER_H_ */
+    Parser();
+
+    bool ParseConfig(const std::string& path);
+    void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+    void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+
+  private:
+    void ParseData(const std::string& filename, const std::string& data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
+
+    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+    std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
new file mode 100644
index 0000000..21adce9
--- /dev/null
+++ b/init/persistent_properties.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 "persistent_properties.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/system_properties.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::ReadFdToString;
+using android::base::StartsWith;
+using android::base::WriteStringToFd;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+std::string persistent_property_filename = "/data/property/persistent_properties";
+
+namespace {
+
+constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
+
+void AddPersistentProperty(const std::string& name, const std::string& value,
+                           PersistentProperties* persistent_properties) {
+    auto persistent_property_record = persistent_properties->add_properties();
+    persistent_property_record->set_name(name);
+    persistent_property_record->set_value(value);
+}
+
+Result<PersistentProperties> LoadLegacyPersistentProperties() {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+    if (!dir) {
+        return ErrnoError() << "Unable to open persistent property directory \""
+                            << kLegacyPersistentPropertyDir << "\"";
+    }
+
+    PersistentProperties persistent_properties;
+    dirent* entry;
+    while ((entry = readdir(dir.get())) != nullptr) {
+        if (!StartsWith(entry->d_name, "persist.")) {
+            continue;
+        }
+        if (entry->d_type != DT_REG) {
+            continue;
+        }
+
+        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
+        if (fd == -1) {
+            PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
+            continue;
+        }
+
+        struct stat sb;
+        if (fstat(fd, &sb) == -1) {
+            PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
+            continue;
+        }
+
+        // File must not be accessible to others, be owned by root/root, and
+        // not be a hard link to any other file.
+        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
+            sb.st_nlink != 1) {
+            PLOG(ERROR) << "skipping insecure property file " << entry->d_name
+                        << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
+                        << " mode=" << std::oct << sb.st_mode << ")";
+            continue;
+        }
+
+        std::string value;
+        if (ReadFdToString(fd, &value)) {
+            AddPersistentProperty(entry->d_name, value, &persistent_properties);
+        } else {
+            PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
+        }
+    }
+    return persistent_properties;
+}
+
+void RemoveLegacyPersistentPropertyFiles() {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+    if (!dir) {
+        PLOG(ERROR) << "Unable to open persistent property directory \""
+                    << kLegacyPersistentPropertyDir << "\"";
+        return;
+    }
+
+    dirent* entry;
+    while ((entry = readdir(dir.get())) != nullptr) {
+        if (!StartsWith(entry->d_name, "persist.")) {
+            continue;
+        }
+        if (entry->d_type != DT_REG) {
+            continue;
+        }
+        unlinkat(dirfd(dir.get()), entry->d_name, 0);
+    }
+}
+
+PersistentProperties LoadPersistentPropertiesFromMemory() {
+    PersistentProperties persistent_properties;
+    __system_property_foreach(
+        [](const prop_info* pi, void* cookie) {
+            __system_property_read_callback(
+                pi,
+                [](void* cookie, const char* name, const char* value, unsigned serial) {
+                    if (StartsWith(name, "persist.")) {
+                        auto properties = reinterpret_cast<PersistentProperties*>(cookie);
+                        AddPersistentProperty(name, value, properties);
+                    }
+                },
+                cookie);
+        },
+        &persistent_properties);
+    return persistent_properties;
+}
+
+Result<std::string> ReadPersistentPropertyFile() {
+    const std::string temp_filename = persistent_property_filename + ".tmp";
+    if (access(temp_filename.c_str(), F_OK) == 0) {
+        LOG(INFO)
+            << "Found temporary property file while attempting to persistent system properties"
+               " a previous persistent property write may have failed";
+        unlink(temp_filename.c_str());
+    }
+    auto file_contents = ReadFile(persistent_property_filename);
+    if (!file_contents) {
+        return Error() << "Unable to read persistent property file: " << file_contents.error();
+    }
+    return *file_contents;
+}
+
+}  // namespace
+
+Result<PersistentProperties> LoadPersistentPropertyFile() {
+    auto file_contents = ReadPersistentPropertyFile();
+    if (!file_contents) return file_contents.error();
+
+    PersistentProperties persistent_properties;
+    if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
+
+    // If the file cannot be parsed in either format, then we don't have any recovery
+    // mechanisms, so we delete it to allow for future writes to take place successfully.
+    unlink(persistent_property_filename.c_str());
+    return Error() << "Unable to parse persistent property file: Could not parse protobuf";
+}
+
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
+    const std::string temp_filename = persistent_property_filename + ".tmp";
+    unique_fd fd(TEMP_FAILURE_RETRY(
+        open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+    if (fd == -1) {
+        return ErrnoError() << "Could not open temporary properties file";
+    }
+    std::string serialized_string;
+    if (!persistent_properties.SerializeToString(&serialized_string)) {
+        return Error() << "Unable to serialize properties";
+    }
+    if (!WriteStringToFd(serialized_string, fd)) {
+        return ErrnoError() << "Unable to write file contents";
+    }
+    fsync(fd);
+    fd.reset();
+
+    if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
+        int saved_errno = errno;
+        unlink(temp_filename.c_str());
+        return Error(saved_errno) << "Unable to rename persistent property file";
+    }
+    return Success();
+}
+
+// Persistent properties are not written often, so we rather not keep any data in memory and read
+// then rewrite the persistent property file for each update.
+void WritePersistentProperty(const std::string& name, const std::string& value) {
+    auto persistent_properties = LoadPersistentPropertyFile();
+
+    if (!persistent_properties) {
+        LOG(ERROR) << "Recovering persistent properties from memory: "
+                   << persistent_properties.error();
+        persistent_properties = LoadPersistentPropertiesFromMemory();
+    }
+    auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
+                           persistent_properties->mutable_properties()->end(),
+                           [&name](const auto& record) { return record.name() == name; });
+    if (it != persistent_properties->mutable_properties()->end()) {
+        it->set_name(name);
+        it->set_value(value);
+    } else {
+        AddPersistentProperty(name, value, &persistent_properties.value());
+    }
+
+    if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
+        LOG(ERROR) << "Could not store persistent property: " << result.error();
+    }
+}
+
+PersistentProperties LoadPersistentProperties() {
+    auto persistent_properties = LoadPersistentPropertyFile();
+
+    if (!persistent_properties) {
+        LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
+                   << persistent_properties.error();
+        persistent_properties = LoadLegacyPersistentProperties();
+        if (!persistent_properties) {
+            LOG(ERROR) << "Unable to load legacy persistent properties: "
+                       << persistent_properties.error();
+            return {};
+        }
+        if (auto result = WritePersistentPropertyFile(*persistent_properties); result) {
+            RemoveLegacyPersistentPropertyFiles();
+        } else {
+            LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
+            // Fall through so that we still set the properties that we've read.
+        }
+    }
+
+    return *persistent_properties;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
new file mode 100644
index 0000000..5f4df85
--- /dev/null
+++ b/init/persistent_properties.h
@@ -0,0 +1,39 @@
+/*
+ * 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_PERSISTENT_PROPERTIES_H
+#define _INIT_PERSISTENT_PROPERTIES_H
+
+#include <string>
+
+#include "result.h"
+#include "system/core/init/persistent_properties.pb.h"
+
+namespace android {
+namespace init {
+
+PersistentProperties LoadPersistentProperties();
+void WritePersistentProperty(const std::string& name, const std::string& value);
+
+// Exposed only for testing
+Result<PersistentProperties> LoadPersistentPropertyFile();
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
+extern std::string persistent_property_filename;
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/libunwindstack/Log.h b/init/persistent_properties.proto
similarity index 64%
copy from libunwindstack/Log.h
copy to init/persistent_properties.proto
index 2d01aa8..c8d2e3a 100644
--- a/libunwindstack/Log.h
+++ b/init/persistent_properties.proto
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 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.
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
 
-#include <stdint.h>
+message PersistentProperties {
+    message PersistentPropertyRecord {
+        optional string name = 1;
+        optional string value = 2;
+    }
 
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
-
-#endif  // _LIBUNWINDSTACK_LOG_H
+    repeated PersistentPropertyRecord properties = 1;
+}
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
new file mode 100644
index 0000000..872e9a1
--- /dev/null
+++ b/init/persistent_properties_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 "persistent_properties.h"
+
+#include <errno.h>
+
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "util.h"
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+PersistentProperties VectorToPersistentProperties(
+    const std::vector<std::pair<std::string, std::string>>& input_properties) {
+    PersistentProperties persistent_properties;
+
+    for (const auto& [name, value] : input_properties) {
+        auto persistent_property_record = persistent_properties.add_properties();
+        persistent_property_record->set_name(name);
+        persistent_property_record->set_value(value);
+    }
+
+    return persistent_properties;
+}
+
+void CheckPropertiesEqual(std::vector<std::pair<std::string, std::string>> expected,
+                          const PersistentProperties& persistent_properties) {
+    for (const auto& persistent_property_record : persistent_properties.properties()) {
+        auto it = std::find_if(expected.begin(), expected.end(),
+                               [persistent_property_record](const auto& entry) {
+                                   return entry.first == persistent_property_record.name() &&
+                                          entry.second == persistent_property_record.value();
+                               });
+        ASSERT_TRUE(it != expected.end())
+            << "Found unexpected property (" << persistent_property_record.name() << ", "
+            << persistent_property_record.value() << ")";
+        expected.erase(it);
+    }
+    auto joiner = [](const std::vector<std::pair<std::string, std::string>>& vector) {
+        std::string result;
+        for (const auto& [name, value] : vector) {
+            result += " (" + name + ", " + value + ")";
+        }
+        return result;
+    };
+    EXPECT_TRUE(expected.empty()) << "Did not find expected properties:" << joiner(expected);
+}
+
+TEST(persistent_properties, EndToEnd) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+        {"persist.sys.locale", "en-US"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+        {"persist.test.empty.value", ""},
+        {"persist.test.new.line", "abc\n\n\nabc"},
+        {"persist.test.numbers", "1234567890"},
+        {"persist.test.non.ascii", "\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F"},
+        // We don't currently allow for non-ascii names for system properties, but this is a policy
+        // decision, not a technical limitation.
+        {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-name"},
+    };
+
+    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    auto read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(persistent_properties, read_back_properties);
+}
+
+TEST(persistent_properties, AddProperty) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+        {"persist.sys.timezone", "America/Los_Angeles"},
+    };
+    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+        {"persist.sys.timezone", "America/Los_Angeles"},
+        {"persist.sys.locale", "pt-BR"},
+    };
+
+    auto read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdateProperty) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+        {"persist.sys.locale", "en-US"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+    };
+    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+        {"persist.sys.locale", "pt-BR"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+    };
+
+    auto read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdatePropertyBadParse) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    ASSERT_TRUE(WriteFile(tf.path, "ab"));
+
+    WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+    auto read_back_properties = LoadPersistentProperties();
+    EXPECT_GT(read_back_properties.properties().size(), 0);
+
+    auto it =
+        std::find_if(read_back_properties.properties().begin(),
+                     read_back_properties.properties().end(), [](const auto& entry) {
+                         return entry.name() == "persist.sys.locale" && entry.value() == "pt-BR";
+                     });
+    EXPECT_FALSE(it == read_back_properties.properties().end());
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 20f21a9..3cf3ab9 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -17,7 +17,6 @@
 #include "property_service.h"
 
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -34,13 +33,16 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
+#include <wchar.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
 #include <memory>
+#include <queue>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -53,21 +55,27 @@
 #include <selinux/selinux.h>
 
 #include "init.h"
+#include "persistent_properties.h"
 #include "util.h"
 
+using android::base::StartsWith;
 using android::base::StringPrintf;
+using android::base::Timer;
 
-#define PERSISTENT_PROPERTY_DIR  "/data/property"
 #define RECOVERY_MOUNT_POINT "/recovery"
 
-static int persistent_properties_loaded = 0;
+namespace android {
+namespace init {
+
+static bool persistent_properties_loaded = false;
 
 static int property_set_fd = -1;
 
+static struct selabel_handle* sehandle_prop;
+
 void property_init() {
     if (__system_property_area_init()) {
-        LOG(ERROR) << "Failed to initialize property area";
-        exit(1);
+        LOG(FATAL) << "Failed to initialize property area";
     }
 }
 
@@ -114,29 +122,6 @@
     return check_mac_perms(ctl_name, sctx, cr);
 }
 
-static void write_persistent_property(const char *name, const char *value)
-{
-    char tempPath[PATH_MAX];
-    char path[PATH_MAX];
-    int fd;
-
-    snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
-    fd = mkstemp(tempPath);
-    if (fd < 0) {
-        PLOG(ERROR) << "Unable to write persistent property to temp file " << tempPath;
-        return;
-    }
-    write(fd, value, strlen(value));
-    fsync(fd);
-    close(fd);
-
-    snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
-    if (rename(tempPath, path)) {
-        PLOG(ERROR) << "Unable to rename persistent property file " << tempPath << " to " << path;
-        unlink(tempPath);
-    }
-}
-
 bool is_legal_property_name(const std::string& name) {
     size_t namelen = name.size();
 
@@ -162,7 +147,7 @@
     return true;
 }
 
-uint32_t property_set(const std::string& name, const std::string& value) {
+static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
     size_t valuelen = value.size();
 
     if (!is_legal_property_name(name)) {
@@ -170,22 +155,22 @@
         return PROP_ERROR_INVALID_NAME;
     }
 
-    if (valuelen >= PROP_VALUE_MAX) {
+    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
         LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                    << "value too long";
         return PROP_ERROR_INVALID_VALUE;
     }
 
-    if (name == "selinux.restorecon_recursive" && valuelen > 0) {
-        if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
-            LOG(ERROR) << "Failed to restorecon_recursive " << value;
-        }
+    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
+                   << "value not a UTF8 encoded string";
+        return PROP_ERROR_INVALID_VALUE;
     }
 
     prop_info* pi = (prop_info*) __system_property_find(name.c_str());
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
-        if (android::base::StartsWith(name, "ro.")) {
+        if (StartsWith(name, "ro.")) {
             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                        << "property already set";
             return PROP_ERROR_READ_ONLY_PROPERTY;
@@ -203,13 +188,92 @@
 
     // Don't write properties to disk until after we have read all default
     // properties to prevent them from being overwritten by default values.
-    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
-        write_persistent_property(name.c_str(), value.c_str());
+    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
+        WritePersistentProperty(name, value);
     }
     property_changed(name, value);
     return PROP_SUCCESS;
 }
 
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+    pid_t pid;
+    PropertyAsyncFunc func;
+    std::string name;
+    std::string value;
+};
+
+static std::queue<PropertyChildInfo> property_children;
+
+static void PropertyChildLaunch() {
+    auto& info = property_children.front();
+    pid_t pid = fork();
+    if (pid < 0) {
+        LOG(ERROR) << "Failed to fork for property_set_async";
+        while (!property_children.empty()) {
+            property_children.pop();
+        }
+        return;
+    }
+    if (pid != 0) {
+        info.pid = pid;
+    } else {
+        if (info.func(info.name, info.value) != 0) {
+            LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
+                       << "\") failed";
+        }
+        _exit(0);
+    }
+}
+
+bool PropertyChildReap(pid_t pid) {
+    if (property_children.empty()) {
+        return false;
+    }
+    auto& info = property_children.front();
+    if (info.pid != pid) {
+        return false;
+    }
+    if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
+        LOG(ERROR) << "Failed to set async property " << info.name;
+    }
+    property_children.pop();
+    if (!property_children.empty()) {
+        PropertyChildLaunch();
+    }
+    return true;
+}
+
+static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
+                                 PropertyAsyncFunc func) {
+    if (value.empty()) {
+        return PropertySetImpl(name, value);
+    }
+
+    PropertyChildInfo info;
+    info.func = func;
+    info.name = name;
+    info.value = value;
+    property_children.push(info);
+    if (property_children.size() == 1) {
+        PropertyChildLaunch();
+    }
+    return PROP_SUCCESS;
+}
+
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+    return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
+uint32_t property_set(const std::string& name, const std::string& value) {
+    if (name == "selinux.restorecon_recursive") {
+        return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+    }
+
+    return PropertySetImpl(name, value);
+}
+
 class SocketConnection {
  public:
   SocketConnection(int socket, const struct ucred& cred)
@@ -276,7 +340,7 @@
     while (*timeout_ms > 0) {
       Timer timer;
       int nr = poll(ufds, 1, *timeout_ms);
-      uint64_t millis = timer.duration_ms();
+      uint64_t millis = timer.duration().count();
       *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
 
       if (nr > 0) {
@@ -345,7 +409,7 @@
   char* source_ctx = nullptr;
   getpeercon(socket.socket(), &source_ctx);
 
-  if (android::base::StartsWith(name, "ctl.")) {
+  if (StartsWith(name, "ctl.")) {
     if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
       handle_control_message(name.c_str() + 4, value.c_str());
       if (!legacy_protocol) {
@@ -363,6 +427,20 @@
     }
   } else {
     if (check_mac_perms(name, source_ctx, &cr)) {
+      // sys.powerctl is a special property that is used to make the device reboot.  We want to log
+      // any process that sets this property to be able to accurately blame the cause of a shutdown.
+      if (name == "sys.powerctl") {
+        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)) {
+          // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
+          process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
+        }
+        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
+                  << process_log_string;
+      }
+
       uint32_t result = property_set(name, value);
       if (!legacy_protocol) {
         socket.SendUint32(result);
@@ -509,73 +587,18 @@
 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
 static bool load_properties_from_file(const char* filename, const char* filter) {
     Timer t;
-    std::string data;
-    std::string err;
-    if (!ReadFile(filename, &data, &err)) {
-        PLOG(WARNING) << "Couldn't load property file: " << err;
+    auto file_contents = ReadFile(filename);
+    if (!file_contents) {
+        PLOG(WARNING) << "Couldn't load property file '" << filename
+                      << "': " << file_contents.error();
         return false;
     }
-    data.push_back('\n');
-    load_properties(&data[0], filter);
+    file_contents->push_back('\n');
+    load_properties(file_contents->data(), filter);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
     return true;
 }
 
-static void load_persistent_properties() {
-    persistent_properties_loaded = 1;
-
-    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
-    if (!dir) {
-        PLOG(ERROR) << "Unable to open persistent property directory \""
-                    << PERSISTENT_PROPERTY_DIR << "\"";
-        return;
-    }
-
-    struct dirent* entry;
-    while ((entry = readdir(dir.get())) != NULL) {
-        if (strncmp("persist.", entry->d_name, strlen("persist."))) {
-            continue;
-        }
-        if (entry->d_type != DT_REG) {
-            continue;
-        }
-
-        // Open the file and read the property value.
-        int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
-        if (fd == -1) {
-            PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
-            continue;
-        }
-
-        struct stat sb;
-        if (fstat(fd, &sb) == -1) {
-            PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
-            close(fd);
-            continue;
-        }
-
-        // File must not be accessible to others, be owned by root/root, and
-        // not be a hard link to any other file.
-        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 || sb.st_nlink != 1) {
-            PLOG(ERROR) << "skipping insecure property file " << entry->d_name
-                        << " (uid=" << sb.st_uid << " gid=" << sb.st_gid
-                        << " nlink=" << sb.st_nlink << " mode=" << std::oct << sb.st_mode << ")";
-            close(fd);
-            continue;
-        }
-
-        char value[PROP_VALUE_MAX];
-        int length = read(fd, value, sizeof(value) - 1);
-        if (length >= 0) {
-            value[length] = 0;
-            property_set(entry->d_name, value);
-        } else {
-            PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
-        }
-        close(fd);
-    }
-}
-
 // persist.sys.usb.config values can't be combined on build-time when property
 // files are split into each partition.
 // So we need to apply the same rule of build/make/tools/post_process_props.py
@@ -618,9 +641,24 @@
  * has mounted /data.
  */
 void load_persist_props(void) {
+    // Devices with FDE have load_persist_props called twice; the first time when the temporary
+    // /data partition is mounted and then again once /data is truly mounted.  We do not want to
+    // read persistent properties from the temporary /data partition or mark persistent properties
+    // as having been loaded during the first call, so we return in that case.
+    std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+    std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+    if (crypto_state == "encrypted" && crypto_type == "block") {
+        static size_t num_calls = 0;
+        if (++num_calls == 1) return;
+    }
+
     load_override_properties();
     /* Read persistent properties after all default values have been loaded. */
-    load_persistent_properties();
+    auto persistent_properties = LoadPersistentProperties();
+    for (const auto& persistent_property_record : persistent_properties.properties()) {
+        property_set(persistent_property_record.name(), persistent_property_record.value());
+    }
+    persistent_properties_loaded = true;
     property_set("ro.persistent_properties.ready", "true");
 }
 
@@ -647,7 +685,7 @@
     boot_img_hdr hdr;
     if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
         std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
-        property_set("ro.recovery_id", hex.c_str());
+        property_set("ro.recovery_id", hex);
     } else {
         PLOG(ERROR) << "error reading /recovery";
     }
@@ -663,17 +701,38 @@
     load_recovery_id_prop();
 }
 
+static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+    property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+
+    if (!d || !d->name || !d->cr) {
+        LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+        return 0;
+    }
+
+    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+             d->cr->gid);
+    return 0;
+}
+
 void start_property_service() {
+    sehandle_prop = selinux_android_prop_context_handle();
+
+    selinux_callback cb;
+    cb.func_audit = SelinuxAuditCallback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
     property_set("ro.property_service.version", "2");
 
     property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, nullptr, sehandle);
+                                   false, 0666, 0, 0, nullptr);
     if (property_set_fd == -1) {
-        PLOG(ERROR) << "start_property_service socket creation failed";
-        exit(1);
+        PLOG(FATAL) << "start_property_service socket creation failed";
     }
 
     listen(property_set_fd, 8);
 
     register_epoll_handler(property_set_fd, handle_property_set_fd);
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index 9a5b6f6..a55e79c 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -21,11 +21,16 @@
 
 #include <string>
 
+namespace android {
+namespace init {
+
 struct property_audit_data {
     ucred *cr;
     const char* name;
 };
 
+extern bool PropertyChildReap(pid_t pid);
+
 void property_init(void);
 void property_load_boot_defaults(void);
 void load_persist_props(void);
@@ -34,5 +39,7 @@
 uint32_t property_set(const std::string& name, const std::string& value);
 bool is_legal_property_name(const std::string& name);
 
+}  // namespace init
+}  // namespace android
 
 #endif  /* _INIT_PROPERTY_H */
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 4d784aa..95dd340 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -21,8 +21,14 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
+#include <android-base/properties.h>
 #include <gtest/gtest.h>
 
+using android::base::SetProperty;
+
+namespace android {
+namespace init {
+
 TEST(property_service, very_long_name_35166374) {
   // Connect to the property service directly...
   int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
@@ -46,3 +52,20 @@
   ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
   ASSERT_EQ(0, close(fd));
 }
+
+TEST(property_service, non_utf8_value) {
+    ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xE0\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xE0\xA0\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x01\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\x80"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "ab\xF0\x90\x80\x80qe\xF0\x90\x80"));
+    EXPECT_TRUE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\x80"));
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index cee5a72..b17dbaf 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,7 +20,7 @@
 #include <fcntl.h>
 #include <linux/fs.h>
 #include <mntent.h>
-#include <selinux/selinux.h>
+#include <sys/capability.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -35,6 +35,7 @@
 #include <thread>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -47,12 +48,20 @@
 #include <fs_mgr.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
 
+#include "capabilities.h"
 #include "init.h"
 #include "property_service.h"
 #include "service.h"
+#include "sigchld_handler.h"
 
+using android::base::Split;
 using android::base::StringPrintf;
+using android::base::Timer;
+
+namespace android {
+namespace init {
 
 // represents umount status during reboot / shutdown.
 enum UmountStat {
@@ -78,8 +87,8 @@
           mnt_type_(entry.mnt_type),
           mnt_opts_(entry.mnt_opts) {}
 
-    bool Umount() {
-        int r = umount2(mnt_dir_.c_str(), 0);
+    bool Umount(bool force) {
+        int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0);
         if (r == 0) {
             LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
             return true;
@@ -157,12 +166,39 @@
 }
 
 static void LogShutdownTime(UmountStat stat, Timer* t) {
-    LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat;
+    LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration().count()) << ":"
+                 << stat;
 }
 
-static void __attribute__((noreturn))
-RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+bool IsRebootCapable() {
+    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
+        PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
+        return true;
+    }
+
+    ScopedCaps caps(cap_get_proc());
+    if (!caps) {
+        PLOG(WARNING) << "cap_get_proc() failed";
+        return true;
+    }
+
+    cap_flag_value_t value = CAP_SET;
+    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
+        PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
+        return true;
+    }
+    return value == CAP_SET;
+}
+
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
     LOG(INFO) << "Reboot ending, jumping to kernel";
+
+    if (!IsRebootCapable()) {
+        // On systems where init does not have the capability of rebooting the
+        // device, just exit cleanly.
+        exit(0);
+    }
+
     switch (cmd) {
         case ANDROID_RB_POWEROFF:
             reboot(RB_POWER_OFF);
@@ -178,7 +214,7 @@
             break;
     }
     // In normal case, reboot should not return.
-    PLOG(FATAL) << "reboot call returned";
+    PLOG(ERROR) << "reboot call returned";
     abort();
 }
 
@@ -227,10 +263,8 @@
     }
 }
 
-static UmountStat UmountPartitions(int timeoutMs) {
+static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
     Timer t;
-    UmountStat stat = UMOUNT_STAT_TIMEOUT;
-    int retry = 0;
     /* data partition needs all pending writes to be completed and all emulated partitions
      * umounted.If the current waiting is not good enough, give
      * up and leave it to e2fsck after reboot to fix it.
@@ -242,25 +276,28 @@
             return UMOUNT_STAT_ERROR;
         }
         if (block_devices.size() == 0) {
-            stat = UMOUNT_STAT_SUCCESS;
-            break;
+            return UMOUNT_STAT_SUCCESS;
         }
-        if ((timeoutMs < t.duration_ms()) && retry > 0) {  // try umount at least once
-            stat = UMOUNT_STAT_TIMEOUT;
-            break;
+        bool unmount_done = true;
+        if (emulated_devices.size() > 0) {
+            unmount_done = std::all_of(emulated_devices.begin(), emulated_devices.end(),
+                                       [](auto& entry) { return entry.Umount(false); });
+            if (unmount_done) {
+                sync();
+            }
         }
-        if (emulated_devices.size() > 0 &&
-            std::all_of(emulated_devices.begin(), emulated_devices.end(),
-                        [](auto& entry) { return entry.Umount(); })) {
-            sync();
+        unmount_done =
+            std::all_of(block_devices.begin(), block_devices.end(),
+                        [&timeout](auto& entry) { return entry.Umount(timeout == 0ms); }) &&
+            unmount_done;
+        if (unmount_done) {
+            return UMOUNT_STAT_SUCCESS;
         }
-        for (auto& entry : block_devices) {
-            entry.Umount();
+        if ((timeout < t.duration())) {  // try umount at least once
+            return UMOUNT_STAT_TIMEOUT;
         }
-        retry++;
         std::this_thread::sleep_for(100ms);
     }
-    return stat;
 }
 
 static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
@@ -274,7 +311,7 @@
  *
  * return true when umount was successful. false when timed out.
  */
-static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
+static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
     Timer t;
     std::vector<MountEntry> block_devices;
     std::vector<MountEntry> emulated_devices;
@@ -285,13 +322,13 @@
         return UMOUNT_STAT_ERROR;
     }
 
-    UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
+    UmountStat stat = UmountPartitions(timeout - t.duration());
     if (stat != UMOUNT_STAT_SUCCESS) {
         LOG(INFO) << "umount timeout, last resort, kill all and try";
         if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
-        UmountStat st = UmountPartitions(0);
+        UmountStat st = UmountPartitions(0ms);
         if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
     }
 
@@ -310,70 +347,83 @@
     Timer t;
     LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
-    android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
-                                     S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
-
-    bool is_thermal_shutdown = false;
-    if (cmd == ANDROID_RB_THERMOFF) {
-        is_thermal_shutdown = true;
-        runFsck = false;
+    // Ensure last reboot reason is reduced to canonical
+    // alias reported in bootloader or system boot reason.
+    size_t skip = 0;
+    std::vector<std::string> reasons = Split(reason, ",");
+    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+         reasons[1] == "hard" || reasons[1] == "warm")) {
+        skip = strlen("reboot,");
     }
+    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+    sync();
 
-    unsigned int shutdown_timeout = 0;  // ms
+    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
+
+    auto shutdown_timeout = 0ms;
     if (!SHUTDOWN_ZERO_TIMEOUT) {
         if (is_thermal_shutdown) {
-            constexpr unsigned int thermal_shutdown_timeout = 1;  // sec
-            shutdown_timeout = thermal_shutdown_timeout * 1000;
+            constexpr unsigned int thermal_shutdown_timeout = 1;
+            shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
         } else {
-            constexpr unsigned int shutdown_timeout_default = 6;  // sec
+            constexpr unsigned int shutdown_timeout_default = 6;
             auto shutdown_timeout_property = android::base::GetUintProperty(
                 "ro.build.shutdown_timeout", shutdown_timeout_default);
-            shutdown_timeout = shutdown_timeout_property * 1000;
+            shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
         }
     }
-    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout << " ms";
+    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
 
     // keep debugging tools until non critical ones are all gone.
     const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
     const std::set<std::string> to_starts{"watchdogd"};
-    ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
+    for (const auto& s : ServiceList::GetInstance()) {
         if (kill_after_apps.count(s->name())) {
             s->SetShutdownCritical();
         } else if (to_starts.count(s->name())) {
-            s->Start();
+            if (auto result = s->Start(); !result) {
+                LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
+                           << "': " << result.error();
+            }
             s->SetShutdownCritical();
         } else if (s->IsShutdownCritical()) {
-            s->Start();  // start shutdown critical service if not started
+            // Start shutdown critical service if not started.
+            if (auto result = s->Start(); !result) {
+                LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
+                           << "': " << result.error();
+            }
         }
-    });
+    }
 
-    Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
-    Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
+    Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
+    Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
     if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
-        ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
-            s->SetShutdownCritical();  // will not check animation class separately
-        });
+        // will not check animation class separately
+        for (const auto& service : ServiceList::GetInstance()) {
+            if (service->classnames().count("animation")) service->SetShutdownCritical();
+        }
     }
 
     // optional shutdown step
     // 1. terminate all services except shutdown critical ones. wait for delay to finish
-    if (shutdown_timeout > 0) {
+    if (shutdown_timeout > 0ms) {
         LOG(INFO) << "terminating init services";
 
         // Ask all services to terminate except shutdown critical ones.
-        ServiceManager::GetInstance().ForEachService([](Service* s) {
+        for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
             if (!s->IsShutdownCritical()) s->Terminate();
-        });
+        }
 
         int service_count = 0;
         // Only wait up to half of timeout here
         auto termination_wait_timeout = shutdown_timeout / 2;
-        while (t.duration_ms() < termination_wait_timeout) {
-            ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+        while (t.duration() < termination_wait_timeout) {
+            ReapAnyOutstandingChildren();
 
             service_count = 0;
-            ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
+            for (const auto& s : ServiceList::GetInstance()) {
                 // Count the number of services running except shutdown critical.
                 // Exclude the console as it will ignore the SIGTERM signal
                 // and not exit.
@@ -382,7 +432,7 @@
                 if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
                     service_count++;
                 }
-            });
+            }
 
             if (service_count == 0) {
                 // All terminable services terminated. We can exit early.
@@ -398,13 +448,13 @@
 
     // minimum safety steps before restarting
     // 2. kill all services except ones that are necessary for the shutdown sequence.
-    ServiceManager::GetInstance().ForEachService([](Service* s) {
+    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
         if (!s->IsShutdownCritical()) s->Stop();
-    });
-    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+    }
+    ReapAnyOutstandingChildren();
 
     // 3. send volume shutdown to vold
-    Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
+    Service* voldService = ServiceList::GetInstance().FindService("vold");
     if (voldService != nullptr && voldService->IsRunning()) {
         ShutdownVold();
         voldService->Stop();
@@ -412,12 +462,12 @@
         LOG(INFO) << "vold not running, skipping vold shutdown";
     }
     // logcat stopped here
-    ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
         if (kill_after_apps.count(s->name())) s->Stop();
-    });
+    }
     // 4. sync, try umount, and optionally run fsck for user shutdown
     sync();
-    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration_ms());
+    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
     sync();
     if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
@@ -429,7 +479,7 @@
 
 bool HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
-    std::vector<std::string> cmd_params = android::base::Split(command, ",");
+    std::vector<std::string> cmd_params = Split(command, ",");
     std::string reboot_target = "";
     bool run_fsck = false;
     bool command_invalid = false;
@@ -438,10 +488,17 @@
         command_invalid = true;
     } else if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
-        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
-            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
-            // Run fsck once the file system is remounted in read-only mode.
-            run_fsck = true;
+        if (cmd_params.size() == 2) {
+            if (cmd_params[1] == "userrequested") {
+                // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+                // Run fsck once the file system is remounted in read-only mode.
+                run_fsck = true;
+            } else if (cmd_params[1] == "thermal") {
+                // Turn off sources of heat immediately.
+                TurnOffBacklight();
+                // run_fsck is false to avoid delay
+                cmd = ANDROID_RB_THERMOFF;
+            }
         }
     } else if (cmd_params[0] == "reboot") {
         cmd = ANDROID_RB_RESTART2;
@@ -457,14 +514,11 @@
                                << err;
                 }
             }
-            // If there is an additional bootloader parameter, pass it along
-            if (cmd_params.size() == 3) {
+            // If there is an additional parameter, pass it along
+            if ((cmd_params.size() == 3) && cmd_params[2].size()) {
                 reboot_target += "," + cmd_params[2];
             }
         }
-    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
-        // run_fsck is false to avoid delay
-        cmd = ANDROID_RB_THERMOFF;
     } else {
         command_invalid = true;
     }
@@ -478,20 +532,22 @@
     // Queue shutdown trigger first
     ActionManager::GetInstance().QueueEventTrigger("shutdown");
     // Queue built-in shutdown_done
-    auto shutdown_handler = [cmd, command, reboot_target,
-                             run_fsck](const std::vector<std::string>&) {
+    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
         DoReboot(cmd, command, reboot_target, run_fsck);
-        return 0;
+        return Success();
     };
     ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
 
     // Skip wait for prop if it is in progress
     ResetWaitForProp();
 
-    // Skip wait for exec if it is in progress
-    if (ServiceManager::GetInstance().IsWaitingForExec()) {
-        ServiceManager::GetInstance().ClearExecWait();
+    // Clear EXEC flag if there is one pending
+    for (const auto& s : ServiceList::GetInstance()) {
+        s->UnSetExec();
     }
 
     return true;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/reboot.h b/init/reboot.h
index b304b3c..1c58bd1 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -19,9 +19,15 @@
 
 #include <string>
 
+namespace android {
+namespace init {
+
+// This is a wrapper around the actual reboot calls.  DoReboot() should be preferred in most cases.
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);
+
 /* Reboot / shutdown the system.
  * cmd ANDROID_RB_* as defined in android_reboot.h
- * reason Reason string like "reboot", "userrequested"
+ * reason Reason string like "reboot", "shutdown,userrequested"
  * rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
  *              empty string.
  * runFsck Whether to run fsck after umount is done.
@@ -32,4 +38,11 @@
 // Parses and handles a setprop sys.powerctl message.
 bool HandlePowerctlMessage(const std::string& command);
 
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+bool IsRebootCapable();
+
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/result.h b/init/result.h
new file mode 100644
index 0000000..fc03962
--- /dev/null
+++ b/init/result.h
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
+// Result<T>::error().
+//
+// ResultError is a type that contains both a std::string describing the error and a copy of errno
+// from when the error occurred.  ResultError can be used in an ostream directly to print its
+// string value.
+//
+// Success is a typedef that aids in creating Result<T> that do not contain a return value.
+// Result<Success> is the correct return type for a function that either returns successfully or
+// returns an error value.  Returning Success() from a function that returns Result<Success> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T.  This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed.  The Error class takes
+// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
+// to the end of the failure string to aid in interacting with C APIs.  Alternatively, an errno
+// value can be directly specified via the Error() constructor.
+//
+// ResultError can be used in the ostream when using Error to construct a Result<T>.  In this case,
+// the string that the ResultError takes is passed through the stream normally, but the errno is
+// passed to the Result<T>.  This can be used to pass errno from a failing C function up multiple
+// callers.
+//
+// ResultError can also directly construct a Result<T>.  This is particularly useful if you have a
+// function that return Result<T> but you have a Result<U> and want to return its error.  In this
+// case, you can return the .error() from the Result<U> to construct the Result<T>.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+//   U output;
+//   if (!SomeOtherCppFunction(input, &output)) {
+//     return Error() << "SomeOtherCppFunction(" << input << ") failed";
+//   }
+//   if (!c_api_function(output)) {
+//     return ErrnoError() << "c_api_function(" << output << ") failed";
+//   }
+//   return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#ifndef _INIT_RESULT_H
+#define _INIT_RESULT_H
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+#include <variant>
+
+namespace android {
+namespace init {
+
+struct ResultError {
+    template <typename T>
+    ResultError(T&& error_string, int error_errno)
+        : error_string(std::forward<T>(error_string)), error_errno(error_errno) {}
+
+    std::string error_string;
+    int error_errno;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+    os << t.error_string;
+    return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, ResultError&& t) {
+    os << std::move(t.error_string);
+    return os;
+}
+
+class Error {
+  public:
+    Error() : errno_(0), append_errno_(false) {}
+    Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+
+    template <typename T>
+    Error&& operator<<(T&& t) {
+        ss_ << std::forward<T>(t);
+        return std::move(*this);
+    }
+
+    Error&& operator<<(const ResultError& result_error) {
+        ss_ << result_error.error_string;
+        errno_ = result_error.error_errno;
+        return std::move(*this);
+    }
+
+    Error&& operator<<(ResultError&& result_error) {
+        ss_ << std::move(result_error.error_string);
+        errno_ = result_error.error_errno;
+        return std::move(*this);
+    }
+
+    const std::string str() const {
+        std::string str = ss_.str();
+        if (append_errno_) {
+            if (str.empty()) {
+                return strerror(errno_);
+            }
+            return str + ": " + strerror(errno_);
+        }
+        return str;
+    }
+
+    int get_errno() const { return errno_; }
+
+    Error(const Error&) = delete;
+    Error(Error&&) = delete;
+    Error& operator=(const Error&) = delete;
+    Error& operator=(Error&&) = delete;
+
+  private:
+    std::stringstream ss_;
+    int errno_;
+    bool append_errno_;
+};
+
+inline Error ErrnoError() {
+    return Error(errno);
+}
+
+template <typename T>
+class Result {
+  public:
+    Result() {}
+
+    template <typename U, typename... V,
+              typename = std::enable_if_t<!(std::is_same_v<std::decay_t<U>, Result<T>> &&
+                                            sizeof...(V) == 0)>>
+    Result(U&& result, V&&... results)
+        : contents_(std::in_place_index_t<0>(), std::forward<U>(result),
+                    std::forward<V>(results)...) {}
+
+    Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
+    Result(const ResultError& result_error)
+        : contents_(std::in_place_index_t<1>(), result_error.error_string,
+                    result_error.error_errno) {}
+    Result(ResultError&& result_error)
+        : contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
+                    result_error.error_errno) {}
+
+    bool has_value() const { return contents_.index() == 0; }
+
+    T& value() & { return std::get<0>(contents_); }
+    const T& value() const & { return std::get<0>(contents_); }
+    T&& value() && { return std::get<0>(std::move(contents_)); }
+    const T&& value() const && { return std::get<0>(std::move(contents_)); }
+
+    const ResultError& error() const & { return std::get<1>(contents_); }
+    ResultError&& error() && { return std::get<1>(std::move(contents_)); }
+    const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
+
+    const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
+    std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
+    const std::string&& error_string() const && {
+        return std::get<1>(std::move(contents_)).error_string;
+    }
+
+    int error_errno() const { return std::get<1>(contents_).error_errno; }
+
+    explicit operator bool() const { return has_value(); }
+
+    T& operator*() & { return value(); }
+    const T& operator*() const & { return value(); }
+    T&& operator*() && { return std::move(value()); }
+    const T&& operator*() const && { return std::move(value()); }
+
+    T* operator->() { return &value(); }
+    const T* operator->() const { return &value(); }
+
+  private:
+    std::variant<T, ResultError> contents_;
+};
+
+using Success = std::monostate;
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/result_test.cpp b/init/result_test.cpp
new file mode 100644
index 0000000..327b444
--- /dev/null
+++ b/init/result_test.cpp
@@ -0,0 +1,333 @@
+/*
+ * 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 "result.h"
+
+#include "errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(result, result_accessors) {
+    Result<std::string> result = "success";
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("success", *result);
+    EXPECT_EQ("success", result.value());
+
+    EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+    ASSERT_TRUE(Result<std::string>("success"));
+    ASSERT_TRUE(Result<std::string>("success").has_value());
+
+    EXPECT_EQ("success", *Result<std::string>("success"));
+    EXPECT_EQ("success", Result<std::string>("success").value());
+
+    EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_success) {
+    Result<Success> result = Success();
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ(Success(), *result);
+    EXPECT_EQ(Success(), result.value());
+}
+
+TEST(result, result_success_rvalue) {
+    // Success() doesn't actually create a Result<Success> object, but rather an object that can be
+    // implicitly constructed into a Result<Success> object.
+
+    auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
+    ASSERT_TRUE(MakeRvalueSuccessResult());
+    ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
+
+    EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
+    EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
+}
+
+TEST(result, result_error) {
+    Result<Success> result = Error() << "failure" << 1;
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ("failure1", result.error_string());
+}
+
+TEST(result, result_error_empty) {
+    Result<Success> result = Error();
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ("", result.error_string());
+}
+
+TEST(result, result_error_rvalue) {
+    // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+    // Under the hood, they are an intermediate class that can be implicitly constructed into a
+    // Result<T>.  This is needed both to create the ostream and because Error() itself, by
+    // definition will not know what the type, T, of the underlying Result<T> object that it would
+    // create is.
+
+    auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
+    ASSERT_FALSE(MakeRvalueErrorResult());
+    ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+    EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
+    EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
+}
+
+TEST(result, result_errno_error) {
+    constexpr int test_errno = 6;
+    errno = test_errno;
+    Result<Success> result = ErrnoError() << "failure" << 1;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_errno_error_no_text) {
+    constexpr int test_errno = 6;
+    errno = test_errno;
+    Result<Success> result = ErrnoError();
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ(strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_error_from_other_result) {
+    auto error_text = "test error"s;
+    Result<Success> result = Error() << error_text;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_error_through_ostream) {
+    auto error_text = "test error"s;
+    Result<Success> result = Error() << error_text;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = Error() << result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+    auto error_text = "test error"s;
+    constexpr int test_errno = 6;
+    errno = 6;
+    Result<Success> result = ErrnoError() << error_text;
+
+    errno = 0;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = Error() << result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
+}
+
+TEST(result, constructor_forwarding) {
+    auto result = Result<std::string>(5, 'a');
+
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+    static size_t constructor_called;
+    static size_t copy_constructor_called;
+    static size_t move_constructor_called;
+    static size_t copy_assignment_called;
+    static size_t move_assignment_called;
+
+    template <typename T>
+    ConstructorTracker(T&& string) : string(string) {
+        ++constructor_called;
+    }
+
+    ConstructorTracker(const ConstructorTracker& ct) {
+        ++copy_constructor_called;
+        string = ct.string;
+    }
+    ConstructorTracker(ConstructorTracker&& ct) noexcept {
+        ++move_constructor_called;
+        string = std::move(ct.string);
+    }
+    ConstructorTracker& operator=(const ConstructorTracker& ct) {
+        ++copy_assignment_called;
+        string = ct.string;
+        return *this;
+    }
+    ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+        ++move_assignment_called;
+        string = std::move(ct.string);
+        return *this;
+    }
+
+    std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+    if (in.empty()) {
+        return "literal string";
+    }
+    if (in == "test2") {
+        return ConstructorTracker(in + in + "2");
+    }
+    ConstructorTracker result(in + " " + in);
+    return result;
+};
+
+TEST(result, no_copy_on_return) {
+    // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+    // then those parameters are forwarded to the construction of Result<T>.
+
+    // If returning an prvalue or xvalue, it will be move constructed during the construction of
+    // Result<T>.
+
+    // This check ensures that that is the case, and particularly that no copy constructors
+    // are called.
+
+    auto result1 = ReturnConstructorTracker("");
+    ASSERT_TRUE(result1);
+    EXPECT_EQ("literal string", result1->string);
+    EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result2 = ReturnConstructorTracker("test2");
+    ASSERT_TRUE(result2);
+    EXPECT_EQ("test2test22", result2->string);
+    EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result3 = ReturnConstructorTracker("test3");
+    ASSERT_TRUE(result3);
+    EXPECT_EQ("test3 test3", result3->string);
+    EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor.  This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+    auto return_result_result_with_success = []() -> Result<Result<Success>> {
+        return Result<Success>();
+    };
+    auto result = return_result_result_with_success();
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(*result);
+
+    auto inner_result = result.value();
+    ASSERT_TRUE(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+    auto return_result_result_with_error = []() -> Result<Result<Success>> {
+        return Result<Success>(ResultError("failure string", 6));
+    };
+    auto result = return_result_result_with_error();
+    ASSERT_TRUE(result);
+    ASSERT_FALSE(*result);
+    EXPECT_EQ("failure string", result->error_string());
+    EXPECT_EQ(6, result->error_errno());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding.  In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+    struct TestStruct {
+        TestStruct(int value) : value_(value) {}
+        TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+        int value_;
+    };
+
+    auto return_test_struct = []() -> Result<TestStruct> { return {Result<TestStruct>(6), 6}; };
+
+    auto result = return_test_struct();
+    ASSERT_TRUE(result);
+    EXPECT_EQ(36, result->value_);
+}
+
+TEST(result, die_on_access_failed_result) {
+    Result<std::string> result = Error();
+    ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+    Result<std::string> result = "success";
+    ASSERT_DEATH(result.error_string(), "");
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
new file mode 100644
index 0000000..fe1d6a7
--- /dev/null
+++ b/init/rlimit_parser.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 "rlimit_parser.h"
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+using android::base::EqualsIgnoreCase;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+// Builtins and service definitions both have their arguments start at 1 and finish at 3.
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args) {
+    static const std::vector<std::pair<const char*, int>> text_to_resources = {
+        {"cpu", 0},       {"fsize", 1}, {"data", 2},    {"stack", 3},
+        {"core", 4},      {"rss", 5},   {"nproc", 6},   {"nofile", 7},
+        {"memlock", 8},   {"as", 9},    {"locks", 10},  {"sigpending", 11},
+        {"msgqueue", 12}, {"nice", 13}, {"rtprio", 14}, {"rttime", 15},
+    };
+
+    int resource;
+
+    if (ParseInt(args[1], &resource)) {
+        if (resource >= RLIM_NLIMITS) {
+            return Error() << "Resource '" << args[1] << "' over the maximum resource value '"
+                           << RLIM_NLIMITS << "'";
+        } else if (resource < 0) {
+            return Error() << "Resource '" << args[1] << "' below the minimum resource value '0'";
+        }
+    } else {
+        std::string resource_string;
+        if (StartsWith(args[1], "RLIM_")) {
+            resource_string = args[1].substr(5);
+        } else {
+            resource_string = args[1];
+        }
+
+        auto it = std::find_if(text_to_resources.begin(), text_to_resources.end(),
+                               [&resource_string](const auto& entry) {
+                                   return EqualsIgnoreCase(resource_string, entry.first);
+                               });
+        if (it == text_to_resources.end()) {
+            return Error() << "Could not parse resource '" << args[1] << "'";
+        }
+
+        resource = it->second;
+    }
+
+    rlimit limit;
+    if (!ParseUint(args[2], &limit.rlim_cur)) {
+        return Error() << "Could not parse soft limit '" << args[2] << "'";
+    }
+    if (!ParseUint(args[3], &limit.rlim_max)) {
+        return Error() << "Could not parse hard limit '" << args[3] << "'";
+    }
+    return {resource, limit};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/signal_handler.h b/init/rlimit_parser.h
similarity index 61%
copy from init/signal_handler.h
copy to init/rlimit_parser.h
index 449b4af..0396463 100644
--- a/init/signal_handler.h
+++ b/init/rlimit_parser.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
@@ -14,9 +14,22 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_RLIMIT_PARSER_H
+#define _INIT_RLIMIT_PARSER_H
 
-void signal_handler_init(void);
+#include <sys/resource.h>
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args);
+
+}  // namespace init
+}  // namespace android
 
 #endif
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
new file mode 100644
index 0000000..f3f9eb4
--- /dev/null
+++ b/init/rlimit_parser_test.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "rlimit_parser.h"
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+void TestRlimitSuccess(std::vector<std::string> input,
+                       const std::pair<int, rlimit>& expected_result) {
+    input.emplace(input.begin(), "");
+    ASSERT_EQ(4U, input.size());
+    auto result = ParseRlimit(input);
+
+    ASSERT_TRUE(result) << "input: " << input[1];
+    const auto& [resource, rlimit] = *result;
+    const auto& [expected_resource, expected_rlimit] = expected_result;
+    EXPECT_EQ(expected_resource, resource);
+    EXPECT_EQ(expected_rlimit.rlim_cur, rlimit.rlim_cur);
+    EXPECT_EQ(expected_rlimit.rlim_max, rlimit.rlim_max);
+}
+
+void TestRlimitFailure(std::vector<std::string> input, const std::string& expected_result) {
+    input.emplace(input.begin(), "");
+    ASSERT_EQ(4U, input.size());
+    auto result = ParseRlimit(input);
+
+    ASSERT_FALSE(result) << "input: " << input[1];
+    EXPECT_EQ(expected_result, result.error_string());
+    EXPECT_EQ(0, result.error_errno());
+}
+
+TEST(rlimit, RlimitSuccess) {
+    const std::vector<std::pair<std::vector<std::string>, std::pair<int, rlimit>>>
+        inputs_and_results = {
+            {{"cpu", "10", "10"}, {0, {10, 10}}},
+            {{"fsize", "10", "10"}, {1, {10, 10}}},
+            {{"data", "10", "10"}, {2, {10, 10}}},
+            {{"stack", "10", "10"}, {3, {10, 10}}},
+            {{"core", "10", "10"}, {4, {10, 10}}},
+            {{"rss", "10", "10"}, {5, {10, 10}}},
+            {{"nproc", "10", "10"}, {6, {10, 10}}},
+            {{"nofile", "10", "10"}, {7, {10, 10}}},
+            {{"memlock", "10", "10"}, {8, {10, 10}}},
+            {{"as", "10", "10"}, {9, {10, 10}}},
+            {{"locks", "10", "10"}, {10, {10, 10}}},
+            {{"sigpending", "10", "10"}, {11, {10, 10}}},
+            {{"msgqueue", "10", "10"}, {12, {10, 10}}},
+            {{"nice", "10", "10"}, {13, {10, 10}}},
+            {{"rtprio", "10", "10"}, {14, {10, 10}}},
+            {{"rttime", "10", "10"}, {15, {10, 10}}},
+
+            {{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
+            {{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
+            {{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
+            {{"RLIM_STACK", "10", "10"}, {3, {10, 10}}},
+            {{"RLIM_CORE", "10", "10"}, {4, {10, 10}}},
+            {{"RLIM_RSS", "10", "10"}, {5, {10, 10}}},
+            {{"RLIM_NPROC", "10", "10"}, {6, {10, 10}}},
+            {{"RLIM_NOFILE", "10", "10"}, {7, {10, 10}}},
+            {{"RLIM_MEMLOCK", "10", "10"}, {8, {10, 10}}},
+            {{"RLIM_AS", "10", "10"}, {9, {10, 10}}},
+            {{"RLIM_LOCKS", "10", "10"}, {10, {10, 10}}},
+            {{"RLIM_SIGPENDING", "10", "10"}, {11, {10, 10}}},
+            {{"RLIM_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
+            {{"RLIM_NICE", "10", "10"}, {13, {10, 10}}},
+            {{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
+            {{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
+
+            {{"0", "10", "10"}, {0, {10, 10}}},
+            {{"1", "10", "10"}, {1, {10, 10}}},
+            {{"2", "10", "10"}, {2, {10, 10}}},
+            {{"3", "10", "10"}, {3, {10, 10}}},
+            {{"4", "10", "10"}, {4, {10, 10}}},
+            {{"5", "10", "10"}, {5, {10, 10}}},
+            {{"6", "10", "10"}, {6, {10, 10}}},
+            {{"7", "10", "10"}, {7, {10, 10}}},
+            {{"8", "10", "10"}, {8, {10, 10}}},
+            {{"9", "10", "10"}, {9, {10, 10}}},
+            {{"10", "10", "10"}, {10, {10, 10}}},
+            {{"11", "10", "10"}, {11, {10, 10}}},
+            {{"12", "10", "10"}, {12, {10, 10}}},
+            {{"13", "10", "10"}, {13, {10, 10}}},
+            {{"14", "10", "10"}, {14, {10, 10}}},
+            {{"15", "10", "10"}, {15, {10, 10}}},
+        };
+
+    for (const auto& [input, expected_result] : inputs_and_results) {
+        TestRlimitSuccess(input, expected_result);
+    }
+}
+
+TEST(rlimit, RlimitFailure) {
+    const std::vector<std::pair<std::vector<std::string>, std::string>> inputs_and_results = {
+        {{"-4", "10", "10"}, "Resource '-4' below the minimum resource value '0'"},
+        {{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
+        {{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
+        {{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
+        {{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
+        {{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
+    };
+
+    for (const auto& [input, expected_result] : inputs_and_results) {
+        TestRlimitFailure(input, expected_result);
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/security.cpp b/init/security.cpp
new file mode 100644
index 0000000..a3494a2
--- /dev/null
+++ b/init/security.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "security.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <fstream>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+// Does nothing if Hardware RNG is not present.
+//
+// Since we don't yet trust the quality of Hardware RNG, these bytes are not
+// mixed into the primary pool of Linux RNG and the entropy estimate is left
+// unmodified.
+//
+// If the HW RNG device /dev/hw_random is present, we require that at least
+// 512 bytes read from it are written into Linux RNG. QA is expected to catch
+// devices/configurations where these I/O operations are blocking for a long
+// time. We do not reboot or halt on failures, as this is a best-effort
+// attempt.
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
+    unique_fd hwrandom_fd(
+        TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (hwrandom_fd == -1) {
+        if (errno == ENOENT) {
+            LOG(INFO) << "/dev/hw_random not found";
+            // It's not an error to not have a Hardware RNG.
+            return Success();
+        }
+        return ErrnoError() << "Failed to open /dev/hw_random";
+    }
+
+    unique_fd urandom_fd(
+        TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (urandom_fd == -1) {
+        return ErrnoError() << "Failed to open /dev/urandom";
+    }
+
+    char buf[512];
+    size_t total_bytes_written = 0;
+    while (total_bytes_written < sizeof(buf)) {
+        ssize_t chunk_size =
+            TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+        if (chunk_size == -1) {
+            return ErrnoError() << "Failed to read from /dev/hw_random";
+        } else if (chunk_size == 0) {
+            return Error() << "Failed to read from /dev/hw_random: EOF";
+        }
+
+        chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+        if (chunk_size == -1) {
+            return ErrnoError() << "Failed to write to /dev/urandom";
+        }
+        total_bytes_written += chunk_size;
+    }
+
+    LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
+    return Success();
+}
+
+static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+    std::ifstream inf(path, std::fstream::in);
+    if (!inf) {
+        LOG(ERROR) << "Cannot open for reading: " << path;
+        return false;
+    }
+
+    int current = max;
+    while (current >= min) {
+        // try to write out new value
+        std::string str_val = std::to_string(current);
+        std::ofstream of(path, std::fstream::out);
+        if (!of) {
+            LOG(ERROR) << "Cannot open for writing: " << path;
+            return false;
+        }
+        of << str_val << std::endl;
+        of.close();
+
+        // check to make sure it was recorded
+        inf.seekg(0);
+        std::string str_rec;
+        inf >> str_rec;
+        if (str_val.compare(str_rec) == 0) {
+            break;
+        }
+        current--;
+    }
+    inf.close();
+
+    if (current < min) {
+        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
+        return false;
+    }
+    return true;
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
+static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
+    std::string path;
+    if (compat) {
+        path = MMAP_RND_COMPAT_PATH;
+    } else {
+        path = MMAP_RND_PATH;
+    }
+
+    return SetHighestAvailableOptionValue(path, min, start);
+}
+
+// Set /proc/sys/vm/mmap_rnd_bits and potentially
+// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+// Returns -1 if unable to set these to an acceptable value.
+//
+// To support this sysctl, the following upstream commits are needed:
+//
+// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
+// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
+// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
+// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
+// ec9ee4acd97c drivers: char: random: add get_random_long()
+// 5ef11c35ce86 mm: ASLR: use get_random_long()
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
+// values are arch-dependent
+#if defined(USER_MODE_LINUX)
+    // uml does not support mmap_rnd_bits
+    return Success();
+#elif defined(__aarch64__)
+    // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
+    if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+        return Success();
+    }
+#elif defined(__x86_64__)
+    // x86_64 supports 28 - 32 bits
+    if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+        return Success();
+    }
+#elif defined(__arm__) || defined(__i386__)
+    // check to see if we're running on 64-bit kernel
+    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+    // supported 32-bit architecture must have 16 bits set
+    if (SetMmapRndBitsMin(16, 16, h64)) {
+        return Success();
+    }
+#elif defined(__mips__) || defined(__mips64__)
+    // TODO: add mips support b/27788820
+    return Success();
+#else
+    LOG(ERROR) << "Unknown architecture";
+#endif
+
+    LOG(FATAL) << "Unable to set adequate mmap entropy value!";
+    return Error();
+}
+
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+// Set kptr_restrict to the highest available level.
+//
+// Aborts if unable to set this to an acceptable value.
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
+    std::string path = KPTR_RESTRICT_PATH;
+
+    if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+        LOG(FATAL) << "Unable to set adequate kptr_restrict value!";
+        return Error();
+    }
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/security.h b/init/security.h
new file mode 100644
index 0000000..6f6b944
--- /dev/null
+++ b/init/security.h
@@ -0,0 +1,36 @@
+/*
+ * 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_SECURITY_H
+#define _INIT_SECURITY_H
+
+#include <string>
+#include <vector>
+
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/selinux.cpp b/init/selinux.cpp
new file mode 100644
index 0000000..c4b01e6
--- /dev/null
+++ b/init/selinux.cpp
@@ -0,0 +1,499 @@
+/*
+ * 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.
+ */
+
+// This file contains the functions that initialize SELinux during boot as well as helper functions
+// for SELinux operation for init.
+
+// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
+// Init loads the SEPolicy from the file system, restores the context of /init based on this
+// SEPolicy, and finally exec()'s itself to run in the proper domain.
+
+// The SEPolicy on Android comes in two variants: monolithic and split.
+
+// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
+// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
+
+// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
+// portion of the policy).  This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
+
+// The split SEPolicy is loaded as described below:
+// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
+//    Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  If these two hashes match, then the
+//    system loads this precompiled_sepolicy directly.
+// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
+//    init needs to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it
+//    is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
+//    and load it.  That function contains even more documentation with the specific implementation
+//    details of how the SEPolicy is compiled if needed.
+
+#include "selinux.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <selinux/android.h>
+
+#include "log.h"
+#include "util.h"
+
+using android::base::Timer;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+namespace {
+
+selabel_handle* sehandle = nullptr;
+
+enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+EnforcingStatus StatusFromCmdline() {
+    EnforcingStatus status = SELINUX_ENFORCING;
+
+    import_kernel_cmdline(false,
+                          [&](const std::string& key, const std::string& value, bool in_qemu) {
+                              if (key == "androidboot.selinux" && value == "permissive") {
+                                  status = SELINUX_PERMISSIVE;
+                              }
+                          });
+
+    return status;
+}
+
+bool IsEnforcing() {
+    if (ALLOW_PERMISSIVE_SELINUX) {
+        return StatusFromCmdline() == SELINUX_ENFORCING;
+    }
+    return true;
+}
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+    // Create a pipe used for redirecting child process's output.
+    // * pipe_fds[0] is the FD the parent will use for reading.
+    // * pipe_fds[1] is the FD the child will use for writing.
+    int pipe_fds[2];
+    if (pipe(pipe_fds) == -1) {
+        PLOG(ERROR) << "Failed to create pipe";
+        return false;
+    }
+
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        PLOG(ERROR) << "Failed to fork for " << filename;
+        return false;
+    }
+
+    if (child_pid == 0) {
+        // fork succeeded -- this is executing in the child process
+
+        // Close the pipe FD not used by this process
+        TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+
+        // Redirect stderr to the pipe FD provided by the parent
+        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+            _exit(127);
+            return false;
+        }
+        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+        if (execv(filename, argv) == -1) {
+            PLOG(ERROR) << "Failed to execve " << filename;
+            return false;
+        }
+        // Unreachable because execve will have succeeded and replaced this code
+        // with child process's code.
+        _exit(127);
+        return false;
+    } else {
+        // fork succeeded -- this is executing in the original/parent process
+
+        // Close the pipe FD not used by this process
+        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+        // Log the redirected output of the child process.
+        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+        // As a result, we're buffering all output and logging it in one go at the end of the
+        // invocation, instead of logging it as it comes in.
+        const int child_out_fd = pipe_fds[0];
+        std::string child_output;
+        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+            PLOG(ERROR) << "Failed to capture full output of " << filename;
+        }
+        TEMP_FAILURE_RETRY(close(child_out_fd));
+        if (!child_output.empty()) {
+            // Log captured output, line by line, because LOG expects to be invoked for each line
+            std::istringstream in(child_output);
+            std::string line;
+            while (std::getline(in, line)) {
+                LOG(ERROR) << filename << ": " << line;
+            }
+        }
+
+        // Wait for child to terminate
+        int status;
+        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+            PLOG(ERROR) << "Failed to wait for " << filename;
+            return false;
+        }
+
+        if (WIFEXITED(status)) {
+            int status_code = WEXITSTATUS(status);
+            if (status_code == 0) {
+                return true;
+            } else {
+                LOG(ERROR) << filename << " exited with status " << status_code;
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+        } else if (WIFSTOPPED(status)) {
+            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+        } else {
+            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+        }
+
+        return false;
+    }
+}
+
+bool ReadFirstLine(const char* file, std::string* line) {
+    line->clear();
+
+    std::string contents;
+    if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
+        return false;
+    }
+    std::istringstream in(contents);
+    std::getline(in, *line);
+    return true;
+}
+
+bool FindPrecompiledSplitPolicy(std::string* file) {
+    file->clear();
+    // If there is an odm partition, precompiled_sepolicy will be in
+    // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
+    static constexpr const char vendor_precompiled_sepolicy[] =
+        "/vendor/etc/selinux/precompiled_sepolicy";
+    static constexpr const char odm_precompiled_sepolicy[] =
+        "/odm/etc/selinux/precompiled_sepolicy";
+    if (access(odm_precompiled_sepolicy, R_OK) == 0) {
+        *file = odm_precompiled_sepolicy;
+    } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
+        *file = vendor_precompiled_sepolicy;
+    } else {
+        PLOG(INFO) << "No precompiled sepolicy";
+        return false;
+    }
+    std::string actual_plat_id;
+    if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+        return false;
+    }
+
+    std::string precompiled_plat_id;
+    std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_sha256;
+        file->clear();
+        return false;
+    }
+    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+        file->clear();
+        return false;
+    }
+    return true;
+}
+
+bool GetVendorMappingVersion(std::string* plat_vers) {
+    if (!ReadFirstLine("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
+        PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
+        return false;
+    }
+    if (plat_vers->empty()) {
+        LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
+        return false;
+    }
+    return true;
+}
+
+constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+bool IsSplitPolicyDevice() {
+    return access(plat_policy_cil_file, R_OK) != -1;
+}
+
+bool LoadSplitPolicy() {
+    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+    // * platform -- policy needed due to logic contained in the system image,
+    // * non-platform -- policy needed due to logic contained in the vendor image,
+    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+    //   with newer versions of platform policy.
+    //
+    // secilc is invoked to compile the above three policy files into a single monolithic policy
+    // file. This file is then loaded into the kernel.
+
+    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
+    // must match the platform policy on the system image.
+    std::string precompiled_sepolicy_file;
+    if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+        unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+        if (fd != -1) {
+            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
+                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
+                return false;
+            }
+            return true;
+        }
+    }
+    // No suitable precompiled policy could be loaded
+
+    LOG(INFO) << "Compiling SELinux policy";
+
+    // Determine the highest policy language version supported by the kernel
+    set_selinuxmnt("/sys/fs/selinux");
+    int max_policy_version = security_policyvers();
+    if (max_policy_version == -1) {
+        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
+        return false;
+    }
+
+    // We store the output of the compilation on /dev because this is the most convenient tmpfs
+    // storage mount available this early in the boot sequence.
+    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+    unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+    if (compiled_sepolicy_fd < 0) {
+        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+        return false;
+    }
+
+    // Determine which mapping file to include
+    std::string vend_plat_vers;
+    if (!GetVendorMappingVersion(&vend_plat_vers)) {
+        return false;
+    }
+    std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+
+    // vendor_sepolicy.cil and nonplat_declaration.cil are the new design to replace
+    // nonplat_sepolicy.cil.
+    std::string nonplat_declaration_cil_file("/vendor/etc/selinux/nonplat_declaration.cil");
+    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
+
+    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
+        // For backward compatibility.
+        // TODO: remove this after no device is using nonplat_sepolicy.cil.
+        vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
+        nonplat_declaration_cil_file.clear();
+    } else if (access(nonplat_declaration_cil_file.c_str(), F_OK) == -1) {
+        LOG(ERROR) << "Missing " << nonplat_declaration_cil_file;
+        return false;
+    }
+
+    // odm_sepolicy.cil is default but optional.
+    std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");
+    if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
+        odm_policy_cil_file.clear();
+    }
+    const std::string version_as_string = std::to_string(max_policy_version);
+
+    // clang-format off
+    std::vector<const char*> compile_args {
+        "/system/bin/secilc",
+        plat_policy_cil_file,
+        "-m", "-M", "true", "-G", "-N",
+        // Target the highest policy language version supported by the kernel
+        "-c", version_as_string.c_str(),
+        mapping_file.c_str(),
+        "-o", compiled_sepolicy,
+        // We don't care about file_contexts output by the compiler
+        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
+    };
+    // clang-format on
+
+    if (!nonplat_declaration_cil_file.empty()) {
+        compile_args.push_back(nonplat_declaration_cil_file.c_str());
+    }
+    if (!vendor_policy_cil_file.empty()) {
+        compile_args.push_back(vendor_policy_cil_file.c_str());
+    }
+    if (!odm_policy_cil_file.empty()) {
+        compile_args.push_back(odm_policy_cil_file.c_str());
+    }
+    compile_args.push_back(nullptr);
+
+    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
+        unlink(compiled_sepolicy);
+        return false;
+    }
+    unlink(compiled_sepolicy);
+
+    LOG(INFO) << "Loading compiled SELinux policy";
+    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+        return false;
+    }
+
+    return true;
+}
+
+bool LoadMonolithicPolicy() {
+    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+    if (selinux_android_load_policy() < 0) {
+        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+        return false;
+    }
+    return true;
+}
+
+bool LoadPolicy() {
+    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
+}
+
+}  // namespace
+
+void SelinuxInitialize() {
+    Timer t;
+
+    LOG(INFO) << "Loading SELinux policy";
+    if (!LoadPolicy()) {
+        LOG(FATAL) << "Unable to load SELinux policy";
+    }
+
+    bool kernel_enforcing = (security_getenforce() == 1);
+    bool is_enforcing = IsEnforcing();
+    if (kernel_enforcing != is_enforcing) {
+        if (security_setenforce(is_enforcing)) {
+            PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+        }
+    }
+
+    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
+        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
+    }
+
+    // init's first stage can't set properties, so pass the time to the second stage.
+    setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
+}
+
+// The files and directories that were created before initial sepolicy load or
+// files on ramdisk need to have their security context restored to the proper
+// value. This must happen before /dev is populated by ueventd.
+void SelinuxRestoreContext() {
+    LOG(INFO) << "Running restorecon...";
+    selinux_android_restorecon("/dev", 0);
+    selinux_android_restorecon("/dev/kmsg", 0);
+    if constexpr (WORLD_WRITABLE_KMSG) {
+        selinux_android_restorecon("/dev/kmsg_debug", 0);
+    }
+    selinux_android_restorecon("/dev/socket", 0);
+    selinux_android_restorecon("/dev/random", 0);
+    selinux_android_restorecon("/dev/urandom", 0);
+    selinux_android_restorecon("/dev/__properties__", 0);
+
+    selinux_android_restorecon("/file_contexts.bin", 0);
+    selinux_android_restorecon("/plat_file_contexts", 0);
+    selinux_android_restorecon("/nonplat_file_contexts", 0);
+    selinux_android_restorecon("/plat_property_contexts", 0);
+    selinux_android_restorecon("/nonplat_property_contexts", 0);
+    selinux_android_restorecon("/plat_seapp_contexts", 0);
+    selinux_android_restorecon("/nonplat_seapp_contexts", 0);
+    selinux_android_restorecon("/plat_service_contexts", 0);
+    selinux_android_restorecon("/nonplat_service_contexts", 0);
+    selinux_android_restorecon("/plat_hwservice_contexts", 0);
+    selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
+    selinux_android_restorecon("/sepolicy", 0);
+    selinux_android_restorecon("/vndservice_contexts", 0);
+
+    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+    selinux_android_restorecon("/dev/device-mapper", 0);
+
+    selinux_android_restorecon("/sbin/mke2fs_static", 0);
+    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
+}
+
+// This function sets up SELinux logging to be written to kmsg, to match init's logging.
+void SelinuxSetupKernelLogging() {
+    selinux_callback cb;
+    cb.func_log = selinux_klog_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+}
+
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value.  selinux_android_restorecon() also needs an sehandle for file context look up.  It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+    sehandle = selinux_android_file_context_handle();
+    selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    char* context;
+    if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    std::vector<const char*> c_aliases;
+    for (const auto& alias : aliases) {
+        c_aliases.emplace_back(alias.c_str());
+    }
+    c_aliases.emplace_back(nullptr);
+
+    char* context;
+    if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/selinux.h b/init/selinux.h
new file mode 100644
index 0000000..7b880ec
--- /dev/null
+++ b/init/selinux.h
@@ -0,0 +1,40 @@
+/*
+ * 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_SELINUX_H
+#define _INIT_SELINUX_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelinuxInitialize();
+void SelinuxRestoreContext();
+
+void SelinuxSetupKernelLogging();
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index 09ebc31..b339bc0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <processgroup/processgroup.h>
@@ -42,29 +43,35 @@
 
 #include "init.h"
 #include "property_service.h"
+#include "rlimit_parser.h"
 #include "util.h"
 
 using android::base::boot_clock;
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::make_scope_guard;
 using android::base::ParseInt;
+using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
-static std::string ComputeContextFromExecutable(std::string& service_name,
-                                                const std::string& service_path) {
+namespace android {
+namespace init {
+
+static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
+                                                        const std::string& service_path) {
     std::string computed_context;
 
     char* raw_con = nullptr;
     char* raw_filecon = nullptr;
 
     if (getcon(&raw_con) == -1) {
-        LOG(ERROR) << "could not get context while starting '" << service_name << "'";
-        return "";
+        return Error() << "Could not get security context";
     }
     std::unique_ptr<char> mycon(raw_con);
 
     if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
-        LOG(ERROR) << "could not get file context while starting '" << service_name << "'";
-        return "";
+        return Error() << "Could not get file context";
     }
     std::unique_ptr<char> filecon(raw_filecon);
 
@@ -76,12 +83,14 @@
         free(new_con);
     }
     if (rc == 0 && computed_context == mycon.get()) {
-        LOG(ERROR) << "service " << service_name << " does not have a SELinux domain defined";
-        return "";
+        return Error() << "File " << service_path << "(labeled \"" << filecon.get()
+                       << "\") has incorrect label or no domain transition from " << mycon.get()
+                       << " to another SELinux domain defined. Have you configured your "
+                          "service correctly? https://source.android.com/security/selinux/"
+                          "device-policy#label_new_services_and_address_denials";
     }
     if (rc < 0) {
-        LOG(ERROR) << "could not get context while starting '" << service_name << "'";
-        return "";
+        return Error() << "Could not get process context";
     }
     return computed_context;
 }
@@ -126,51 +135,34 @@
     }
 }
 
-static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) {
+static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
     std::vector<std::string> expanded_args;
+    std::vector<char*> c_strings;
+
     expanded_args.resize(args.size());
-    strs->push_back(const_cast<char*>(args[0].c_str()));
+    c_strings.push_back(const_cast<char*>(args[0].data()));
     for (std::size_t i = 1; i < args.size(); ++i) {
         if (!expand_props(args[i], &expanded_args[i])) {
             LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
         }
-        strs->push_back(const_cast<char*>(expanded_args[i].c_str()));
+        c_strings.push_back(expanded_args[i].data());
     }
-    strs->push_back(nullptr);
+    c_strings.push_back(nullptr);
+
+    return execv(c_strings[0], c_strings.data()) == 0;
 }
 
-ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
-}
+unsigned long Service::next_start_order_ = 1;
+bool Service::is_exec_service_running_ = false;
 
-ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
-                                               const std::string& value)
-    : name(name), value(value) {
-}
-
-Service::Service(const std::string& name, const std::vector<std::string>& args)
-    : name_(name),
-      classnames_({"default"}),
-      flags_(0),
-      pid_(0),
-      crash_count_(0),
-      uid_(0),
-      gid_(0),
-      namespace_flags_(0),
-      seclabel_(""),
-      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
-      keychord_id_(0),
-      ioprio_class_(IoSchedClass_NONE),
-      ioprio_pri_(0),
-      priority_(0),
-      oom_score_adjust_(-1000),
-      args_(args) {
-    onrestart_.InitSingleTrigger("onrestart");
-}
+Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+                 const std::vector<std::string>& args)
+    : Service(name, 0, 0, 0, {}, 0, 0, "", subcontext_for_restart_commands, args) {}
 
 Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
                  const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
                  unsigned namespace_flags, const std::string& seclabel,
-                 const std::vector<std::string>& args)
+                 Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args)
     : name_(name),
       classnames_({"default"}),
       flags_(flags),
@@ -182,12 +174,16 @@
       capabilities_(capabilities),
       namespace_flags_(namespace_flags),
       seclabel_(seclabel),
-      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
+      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0),
       keychord_id_(0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
       priority_(0),
       oom_score_adjust_(-1000),
+      swappiness_(-1),
+      soft_limit_in_bytes_(-1),
+      limit_in_bytes_(-1),
+      start_order_(0),
       args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
@@ -198,13 +194,15 @@
         return;
     }
 
-    std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
-    property_set(prop_name.c_str(), new_state.c_str());
+    std::string prop_name = "init.svc." + name_;
+    property_set(prop_name, new_state);
 
     if (new_state == "running") {
         uint64_t start_ns = time_started_.time_since_epoch().count();
-        property_set(StringPrintf("ro.boottime.%s", name_.c_str()).c_str(),
-                     StringPrintf("%" PRIu64, start_ns).c_str());
+        std::string boottime_property = "ro.boottime." + name_;
+        if (GetProperty(boottime_property, "").empty()) {
+            property_set(boottime_property, std::to_string(start_ns));
+        }
     }
 }
 
@@ -228,10 +226,23 @@
 }
 
 void Service::SetProcessAttributes() {
+    for (const auto& rlimit : rlimits_) {
+        if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+            LOG(FATAL) << StringPrintf("setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed",
+                                       rlimit.first, rlimit.second.rlim_cur, rlimit.second.rlim_max);
+        }
+    }
     // Keep capabilites on uid change.
     if (capabilities_.any() && uid_) {
-        if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED) != 0) {
-            PLOG(FATAL) << "prtcl(PR_SET_KEEPCAPS) failed for " << name_;
+        // If Android is running in a container, some securebits might already
+        // be locked, so don't change those.
+        unsigned long securebits = prctl(PR_GET_SECUREBITS);
+        if (securebits == -1UL) {
+            PLOG(FATAL) << "prctl(PR_GET_SECUREBITS) failed for " << name_;
+        }
+        securebits |= SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED;
+        if (prctl(PR_SET_SECUREBITS, securebits) != 0) {
+            PLOG(FATAL) << "prctl(PR_SET_SECUREBITS) failed for " << name_;
         }
     }
 
@@ -277,12 +288,13 @@
     std::for_each(descriptors_.begin(), descriptors_.end(),
                   std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
 
-    if (flags_ & SVC_TEMPORARY) {
-        return;
-    }
+    if (flags_ & SVC_EXEC) UnSetExec();
+
+    if (flags_ & SVC_TEMPORARY) return;
 
     pid_ = 0;
     flags_ &= (~SVC_RUNNING);
+    start_order_ = 0;
 
     // Oneshot processes go into the disabled state on exit,
     // except when manually restarted.
@@ -301,8 +313,7 @@
     if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
         if (now < time_crashed_ + 4min) {
             if (++crash_count_ > 4) {
-                LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
-                panic();
+                LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
             }
         } else {
             time_crashed_ = now;
@@ -322,18 +333,18 @@
 
 void Service::DumpState() const {
     LOG(INFO) << "service " << name_;
-    LOG(INFO) << "  class '" << android::base::Join(classnames_, " ") << "'";
-    LOG(INFO) << "  exec "<< android::base::Join(args_, " ");
+    LOG(INFO) << "  class '" << Join(classnames_, " ") << "'";
+    LOG(INFO) << "  exec " << Join(args_, " ");
     std::for_each(descriptors_.begin(), descriptors_.end(),
                   [] (const auto& info) { LOG(INFO) << *info; });
 }
 
-bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
     capabilities_ = 0;
 
     if (!CapAmbientSupported()) {
-        *err = "capabilities requested but the kernel does not support ambient capabilities";
-        return false;
+        return Error()
+               << "capabilities requested but the kernel does not support ambient capabilities";
     }
 
     unsigned int last_valid_cap = GetLastValidCap();
@@ -345,74 +356,71 @@
         const std::string& arg = args[i];
         int res = LookupCap(arg);
         if (res < 0) {
-            *err = StringPrintf("invalid capability '%s'", arg.c_str());
-            return false;
+            return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
         }
         unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.
         if (cap > last_valid_cap) {
-            *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
-            return false;
+            return Error() << StringPrintf("capability '%s' not supported by the kernel",
+                                           arg.c_str());
         }
         capabilities_[cap] = true;
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
     classnames_ = std::set<std::string>(args.begin() + 1, args.end());
-    return true;
+    return Success();
 }
 
-bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
     flags_ |= SVC_CONSOLE;
     console_ = args.size() > 1 ? "/dev/" + args[1] : "";
-    return true;
+    return Success();
 }
 
-bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
     flags_ |= SVC_CRITICAL;
-    return true;
+    return Success();
 }
 
-bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
     flags_ |= SVC_DISABLED;
     flags_ |= SVC_RC_DISABLED;
-    return true;
+    return Success();
 }
 
-bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &gid_, &decode_uid_err)) {
-        *err = "Unable to find GID for '" + args[1] + "': " + decode_uid_err;
-        return false;
+Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+    auto gid = DecodeUid(args[1]);
+    if (!gid) {
+        return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
     }
+    gid_ = *gid;
+
     for (std::size_t n = 2; n < args.size(); n++) {
-        gid_t gid;
-        if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
-            *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
-            return false;
+        gid = DecodeUid(args[n]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
         }
-        supp_gids_.emplace_back(gid);
+        supp_gids_.emplace_back(*gid);
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
     priority_ = 0;
     if (!ParseInt(args[1], &priority_,
                   static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
                   static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
-        *err = StringPrintf("process priority value must be range %d - %d",
-                ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
-        return false;
+        return Error() << StringPrintf("process priority value must be range %d - %d",
+                                       ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
     if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
-        *err = "priority value must be range 0 - 7";
-        return false;
+        return Error() << "priority value must be range 0 - 7";
     }
 
     if (args[1] == "rt") {
@@ -422,14 +430,13 @@
     } else if (args[1] == "idle") {
         ioprio_class_ = IoSchedClass_IDLE;
     } else {
-        *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
-        return false;
+        return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
     }
 
-    return true;
+    return Success();
 }
 
-bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
     for (std::size_t i = 1; i < args.size(); i++) {
         int code;
         if (ParseInt(args[i], &code)) {
@@ -438,22 +445,24 @@
             LOG(WARNING) << "ignoring invalid keycode: " << args[i];
         }
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
     flags_ |= SVC_ONESHOT;
-    return true;
+    return Success();
 }
 
-bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
     std::vector<std::string> str_args(args.begin() + 1, args.end());
     int line = onrestart_.NumCommands() + 1;
-    onrestart_.AddCommand(str_args, line, err);
-    return true;
+    if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+        return Error() << "cannot add Onrestart command: " << result.error();
+    }
+    return Success();
 }
 
-bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
     for (size_t i = 1; i < args.size(); i++) {
         if (args[i] == "pid") {
             namespace_flags_ |= CLONE_NEWPID;
@@ -462,112 +471,133 @@
         } else if (args[i] == "mnt") {
             namespace_flags_ |= CLONE_NEWNS;
         } else {
-            *err = "namespace must be 'pid' or 'mnt'";
-            return false;
+            return Error() << "namespace must be 'pid' or 'mnt'";
         }
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
-        *err = "oom_score_adjust value must be in range -1000 - +1000";
-        return false;
+        return Error() << "oom_score_adjust value must be in range -1000 - +1000";
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
+    if (!ParseInt(args[1], &swappiness_, 0)) {
+        return Error() << "swappiness value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
+    if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
+        return Error() << "limit_in_bytes value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
+    if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
+        return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args) {
+    auto rlimit = ParseRlimit(args);
+    if (!rlimit) return rlimit.error();
+
+    rlimits_.emplace_back(*rlimit);
+    return Success();
+}
+
+Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
     seclabel_ = args[1];
-    return true;
+    return Success();
 }
 
-bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
-    envvars_.emplace_back(args[1], args[2]);
-    return true;
+Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
+    environment_vars_.emplace_back(args[1], args[2]);
+    return Success();
 }
 
-bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
     if (args[1] == "critical") {
         flags_ |= SVC_SHUTDOWN_CRITICAL;
-        return true;
+        return Success();
     }
-    return false;
+    return Error() << "Invalid shutdown option";
 }
 
 template <typename T>
-bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
-    uid_t uid = 0;
-    gid_t gid = 0;
+    Result<uid_t> uid = 0;
+    Result<gid_t> gid = 0;
     std::string context = args.size() > 6 ? args[6] : "";
 
-    std::string decode_uid_err;
     if (args.size() > 4) {
-        if (!DecodeUid(args[4], &uid, &decode_uid_err)) {
-            *err = "Unable to find UID for '" + args[4] + "': " + decode_uid_err;
-            return false;
+        uid = DecodeUid(args[4]);
+        if (!uid) {
+            return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
         }
     }
 
     if (args.size() > 5) {
-        if (!DecodeUid(args[5], &gid, &decode_uid_err)) {
-            *err = "Unable to find GID for '" + args[5] + "': " + decode_uid_err;
-            return false;
+        gid = DecodeUid(args[5]);
+        if (!gid) {
+            return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
         }
     }
 
-    auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+    auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
 
     auto old =
         std::find_if(descriptors_.begin(), descriptors_.end(),
                      [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
 
     if (old != descriptors_.end()) {
-        *err = "duplicate descriptor " + args[1] + " " + args[2];
-        return false;
+        return Error() << "duplicate descriptor " << args[1] << " " << args[2];
     }
 
     descriptors_.emplace_back(std::move(descriptor));
-    return true;
+    return Success();
 }
 
 // name type perm [ uid gid context ]
-bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
-    if (!android::base::StartsWith(args[2], "dgram") &&
-        !android::base::StartsWith(args[2], "stream") &&
-        !android::base::StartsWith(args[2], "seqpacket")) {
-        *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
-        return false;
+Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
+    if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
+        !StartsWith(args[2], "seqpacket")) {
+        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
     }
-    return AddDescriptor<SocketInfo>(args, err);
+    return AddDescriptor<SocketInfo>(args);
 }
 
 // name type perm [ uid gid context ]
-bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
-        *err = "file type must be 'r', 'w' or 'rw'";
-        return false;
+        return Error() << "file type must be 'r', 'w' or 'rw'";
     }
     if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
-        *err = "file name must not be relative";
-        return false;
+        return Error() << "file name must not be relative";
     }
-    return AddDescriptor<FileInfo>(args, err);
+    return AddDescriptor<FileInfo>(args);
 }
 
-bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &uid_, &decode_uid_err)) {
-        *err = "Unable to find UID for '" + args[1] + "': " + decode_uid_err;
-        return false;
+Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+    auto uid = DecodeUid(args[1]);
+    if (!uid) {
+        return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
     }
-    return true;
+    uid_ = *uid;
+    return Success();
 }
 
-bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
     writepid_files_.assign(args.begin() + 1, args.end());
-    return true;
+    return Success();
 }
 
 class Service::OptionParserMap : public KeywordMap<OptionParser> {
@@ -596,7 +626,14 @@
         {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
         {"oom_score_adjust",
                         {1,     1,    &Service::ParseOomScoreAdjust}},
+        {"memcg.swappiness",
+                        {1,     1,    &Service::ParseMemcgSwappiness}},
+        {"memcg.soft_limit_in_bytes",
+                        {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
+        {"memcg.limit_in_bytes",
+                        {1,     1,    &Service::ParseMemcgLimitInBytes}},
         {"namespace",   {1,     2,    &Service::ParseNamespace}},
+        {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
         {"shutdown",    {1,     1,    &Service::ParseShutdown}},
@@ -609,30 +646,33 @@
     return option_parsers;
 }
 
-bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
     static const OptionParserMap parser_map;
-    auto parser = parser_map.FindFunction(args, err);
+    auto parser = parser_map.FindFunction(args);
 
-    if (!parser) {
-        return false;
-    }
+    if (!parser) return parser.error();
 
-    return (this->*parser)(args, err);
+    return std::invoke(*parser, this, args);
 }
 
-bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
-    flags_ |= SVC_EXEC | SVC_ONESHOT;
+Result<Success> Service::ExecStart() {
+    flags_ |= SVC_ONESHOT;
 
-    exec_waiter->reset(new Timer);
-
-    if (!Start()) {
-        exec_waiter->reset();
-        return false;
+    if (auto result = Start(); !result) {
+        return result;
     }
-    return true;
+
+    flags_ |= SVC_EXEC;
+    is_exec_service_running_ = true;
+
+    LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
+              << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
+              << ") started; waiting...";
+
+    return Success();
 }
 
-bool Service::Start() {
+Result<Success> Service::Start() {
     // Starting a service removes it from the disabled or reset state and
     // immediately takes it out of the restarting state if it was in there.
     flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
@@ -641,7 +681,8 @@
     // process of exiting, we've ensured that they will immediately restart
     // on exit, unless they are ONESHOT.
     if (flags_ & SVC_RUNNING) {
-        return false;
+        // It is not an error to try to start a service that is already running.
+        return Success();
     }
 
     bool needs_console = (flags_ & SVC_CONSOLE);
@@ -654,28 +695,27 @@
         // properly registered for the device node
         int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
         if (console_fd < 0) {
-            PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
             flags_ |= SVC_DISABLED;
-            return false;
+            return ErrnoError() << "Couldn't open console '" << console_ << "'";
         }
         close(console_fd);
     }
 
     struct stat sb;
     if (stat(args_[0].c_str(), &sb) == -1) {
-        PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
         flags_ |= SVC_DISABLED;
-        return false;
+        return ErrnoError() << "Cannot find '" << args_[0] << "'";
     }
 
     std::string scon;
     if (!seclabel_.empty()) {
         scon = seclabel_;
     } else {
-        scon = ComputeContextFromExecutable(name_, args_[0]);
-        if (scon == "") {
-            return false;
+        auto result = ComputeContextFromExecutable(name_, args_[0]);
+        if (!result) {
+            return result.error();
         }
+        scon = *result;
     }
 
     LOG(INFO) << "starting service '" << name_ << "'...";
@@ -696,8 +736,8 @@
             SetUpPidNamespace(name_);
         }
 
-        for (const auto& ei : envvars_) {
-            add_environment(ei.name.c_str(), ei.value.c_str());
+        for (const auto& [key, value] : environment_vars_) {
+            setenv(key.c_str(), value.c_str(), 1);
         }
 
         std::for_each(descriptors_.begin(), descriptors_.end(),
@@ -705,13 +745,13 @@
 
         // See if there were "writepid" instructions to write to files under /dev/cpuset/.
         auto cpuset_predicate = [](const std::string& path) {
-            return android::base::StartsWith(path, "/dev/cpuset/");
+            return StartsWith(path, "/dev/cpuset/");
         };
         auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
         if (iter == writepid_files_.end()) {
             // There were no "writepid" instructions for cpusets, check if the system default
             // cpuset is specified to be used for the process.
-            std::string default_cpuset = android::base::GetProperty("ro.cpuset.default", "");
+            std::string default_cpuset = GetProperty("ro.cpuset.default", "");
             if (!default_cpuset.empty()) {
                 // Make sure the cpuset name starts and ends with '/'.
                 // A single '/' means the 'root' cpuset.
@@ -725,7 +765,7 @@
                     StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
             }
         }
-        std::string pid_str = StringPrintf("%d", getpid());
+        std::string pid_str = std::to_string(getpid());
         for (const auto& file : writepid_files_) {
             if (!WriteStringToFile(pid_str, file)) {
                 PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
@@ -750,23 +790,20 @@
         // priority. Aborts on failure.
         SetProcessAttributes();
 
-        std::vector<char*> strs;
-        ExpandArgs(args_, &strs);
-        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
-            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
+        if (!ExpandArgsAndExecv(args_)) {
+            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
         }
 
         _exit(127);
     }
 
     if (pid < 0) {
-        PLOG(ERROR) << "failed to fork for '" << name_ << "'";
         pid_ = 0;
-        return false;
+        return ErrnoError() << "Failed to fork";
     }
 
     if (oom_score_adjust_ != -1000) {
-        std::string oom_str = StringPrintf("%d", oom_score_adjust_);
+        std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
         if (!WriteStringToFile(oom_str, oom_file)) {
             PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
@@ -776,39 +813,52 @@
     time_started_ = boot_clock::now();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
+    start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
     errno = -createProcessGroup(uid_, pid_);
     if (errno != 0) {
         PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
                     << name_ << "'";
-    }
+    } else {
+        if (swappiness_ != -1) {
+            if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
+                PLOG(ERROR) << "setProcessGroupSwappiness failed";
+            }
+        }
 
-    if ((flags_ & SVC_EXEC) != 0) {
-        LOG(INFO) << android::base::StringPrintf(
-            "SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...", pid_, uid_, gid_,
-            supp_gids_.size(), !seclabel_.empty() ? seclabel_.c_str() : "default");
+        if (soft_limit_in_bytes_ != -1) {
+            if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
+                PLOG(ERROR) << "setProcessGroupSoftLimit failed";
+            }
+        }
+
+        if (limit_in_bytes_ != -1) {
+            if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+                PLOG(ERROR) << "setProcessGroupLimit failed";
+            }
+        }
     }
 
     NotifyStateChange("running");
-    return true;
+    return Success();
 }
 
-bool Service::StartIfNotDisabled() {
+Result<Success> Service::StartIfNotDisabled() {
     if (!(flags_ & SVC_DISABLED)) {
         return Start();
     } else {
         flags_ |= SVC_DISABLED_START;
     }
-    return true;
+    return Success();
 }
 
-bool Service::Enable() {
+Result<Success> Service::Enable() {
     flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
     if (flags_ & SVC_DISABLED_START) {
         return Start();
     }
-    return true;
+    return Success();
 }
 
 void Service::Reset() {
@@ -834,26 +884,12 @@
         StopOrReset(SVC_RESTART);
     } else if (!(flags_ & SVC_RESTARTING)) {
         /* Just start the service since it's not running. */
-        Start();
+        if (auto result = Start(); !result) {
+            LOG(ERROR) << "Could not restart '" << name_ << "': " << result.error();
+        }
     } /* else: Service is restarting anyways. */
 }
 
-void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
-    boot_clock::time_point now = boot_clock::now();
-    boot_clock::time_point next_start = time_started_ + 5s;
-    if (now > next_start) {
-        flags_ &= (~SVC_RESTARTING);
-        Start();
-        return;
-    }
-
-    time_t next_start_time_t = time(nullptr) +
-        time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
-    if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
-        *process_needs_restart_at = next_start_time_t;
-    }
-}
-
 // The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART.
 void Service::StopOrReset(int how) {
     // The service is still SVC_RUNNING until its process exits, but if it has
@@ -899,50 +935,18 @@
     close(fd);
 }
 
-int ServiceManager::exec_count_ = 0;
+ServiceList::ServiceList() {}
 
-ServiceManager::ServiceManager() {
-}
-
-ServiceManager& ServiceManager::GetInstance() {
-    static ServiceManager instance;
+ServiceList& ServiceList::GetInstance() {
+    static ServiceList instance;
     return instance;
 }
 
-void ServiceManager::AddService(std::unique_ptr<Service> service) {
+void ServiceList::AddService(std::unique_ptr<Service> service) {
     services_.emplace_back(std::move(service));
 }
 
-bool ServiceManager::Exec(const std::vector<std::string>& args) {
-    Service* svc = MakeExecOneshotService(args);
-    if (!svc) {
-        LOG(ERROR) << "Could not create exec service";
-        return false;
-    }
-    if (!svc->ExecStart(&exec_waiter_)) {
-        LOG(ERROR) << "Could not start exec service";
-        ServiceManager::GetInstance().RemoveService(*svc);
-        return false;
-    }
-    return true;
-}
-
-bool ServiceManager::ExecStart(const std::string& name) {
-    Service* svc = FindServiceByName(name);
-    if (!svc) {
-        LOG(ERROR) << "ExecStart(" << name << "): Service not found";
-        return false;
-    }
-    if (!svc->ExecStart(&exec_waiter_)) {
-        LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
-        return false;
-    }
-    return true;
-}
-
-bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
-
-Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
+std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
     // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
     // SECLABEL can be a - to denote default
     std::size_t command_arg = 1;
@@ -963,11 +967,11 @@
     }
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
-    exec_count_++;
-    std::string name =
-        "exec " + std::to_string(exec_count_) + " (" + android::base::Join(str_args, " ") + ")";
+    static size_t exec_count = 0;
+    exec_count++;
+    std::string name = "exec " + std::to_string(exec_count) + " (" + Join(str_args, " ") + ")";
 
-    unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
+    unsigned flags = SVC_ONESHOT | SVC_TEMPORARY;
     CapSet no_capabilities;
     unsigned namespace_flags = 0;
 
@@ -975,100 +979,50 @@
     if (command_arg > 2 && args[1] != "-") {
         seclabel = args[1];
     }
-    uid_t uid = 0;
+    Result<uid_t> uid = 0;
     if (command_arg > 3) {
-        std::string decode_uid_err;
-        if (!DecodeUid(args[2], &uid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find UID for '" << args[2] << "': " << decode_uid_err;
+        uid = DecodeUid(args[2]);
+        if (!uid) {
+            LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
             return nullptr;
         }
     }
-    gid_t gid = 0;
+    Result<gid_t> gid = 0;
     std::vector<gid_t> supp_gids;
     if (command_arg > 4) {
-        std::string decode_uid_err;
-        if (!DecodeUid(args[3], &gid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+        gid = DecodeUid(args[3]);
+        if (!gid) {
+            LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
             return nullptr;
         }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
-            gid_t supp_gid;
-            if (!DecodeUid(args[4 + i], &supp_gid, &decode_uid_err)) {
-                LOG(ERROR) << "Unable to find UID for '" << args[4 + i] << "': " << decode_uid_err;
+            auto supp_gid = DecodeUid(args[4 + i]);
+            if (!supp_gid) {
+                LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
+                           << "': " << supp_gid.error();
                 return nullptr;
             }
-            supp_gids.push_back(supp_gid);
+            supp_gids.push_back(*supp_gid);
         }
     }
 
-    auto svc_p = std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
-                                           namespace_flags, seclabel, str_args);
-    Service* svc = svc_p.get();
-    services_.emplace_back(std::move(svc_p));
-
-    return svc;
+    return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
+                                     namespace_flags, seclabel, nullptr, str_args);
 }
 
-Service* ServiceManager::FindServiceByName(const std::string& name) const {
-    auto svc = std::find_if(services_.begin(), services_.end(),
-                            [&name] (const std::unique_ptr<Service>& s) {
-                                return name == s->name();
-                            });
-    if (svc != services_.end()) {
-        return svc->get();
+// Shutdown services in the opposite order that they were started.
+const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
+    std::vector<Service*> shutdown_services;
+    for (const auto& service : services_) {
+        if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
     }
-    return nullptr;
+    std::sort(shutdown_services.begin(), shutdown_services.end(),
+              [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
+    return shutdown_services;
 }
 
-Service* ServiceManager::FindServiceByPid(pid_t pid) const {
-    auto svc = std::find_if(services_.begin(), services_.end(),
-                            [&pid] (const std::unique_ptr<Service>& s) {
-                                return s->pid() == pid;
-                            });
-    if (svc != services_.end()) {
-        return svc->get();
-    }
-    return nullptr;
-}
-
-Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
-    auto svc = std::find_if(services_.begin(), services_.end(),
-                            [&keychord_id] (const std::unique_ptr<Service>& s) {
-                                return s->keychord_id() == keychord_id;
-                            });
-
-    if (svc != services_.end()) {
-        return svc->get();
-    }
-    return nullptr;
-}
-
-void ServiceManager::ForEachService(const std::function<void(Service*)>& callback) const {
-    for (const auto& s : services_) {
-        callback(s.get());
-    }
-}
-
-void ServiceManager::ForEachServiceInClass(const std::string& classname,
-                                           void (*func)(Service* svc)) const {
-    for (const auto& s : services_) {
-        if (s->classnames().find(classname) != s->classnames().end()) {
-            func(s.get());
-        }
-    }
-}
-
-void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
-                                             void (*func)(Service* svc)) const {
-    for (const auto& s : services_) {
-        if (s->flags() & matchflags) {
-            func(s.get());
-        }
-    }
-}
-
-void ServiceManager::RemoveService(const Service& svc) {
+void ServiceList::RemoveService(const Service& svc) {
     auto svc_it = std::find_if(services_.begin(), services_.end(),
                                [&svc] (const std::unique_ptr<Service>& s) {
                                    return svc.name() == s->name();
@@ -1080,108 +1034,50 @@
     services_.erase(svc_it);
 }
 
-void ServiceManager::DumpState() const {
+void ServiceList::DumpState() const {
     for (const auto& s : services_) {
         s->DumpState();
     }
 }
 
-bool ServiceManager::ReapOneProcess() {
-    int status;
-    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
-    if (pid == 0) {
-        return false;
-    } else if (pid == -1) {
-        PLOG(ERROR) << "waitpid failed";
-        return false;
-    }
-
-    Service* svc = FindServiceByPid(pid);
-
-    std::string name;
-    std::string wait_string;
-    if (svc) {
-        name = android::base::StringPrintf("Service '%s' (pid %d)",
-                                           svc->name().c_str(), pid);
-        if (svc->flags() & SVC_EXEC) {
-            wait_string =
-                android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());
-        }
-    } else {
-        name = android::base::StringPrintf("Untracked pid %d", pid);
-    }
-
-    if (WIFEXITED(status)) {
-        LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
-    } else if (WIFSIGNALED(status)) {
-        LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
-    } else if (WIFSTOPPED(status)) {
-        LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;
-    } else {
-        LOG(INFO) << name << " state changed" << wait_string;
-    }
-
-    if (!svc) {
-        return true;
-    }
-
-    svc->Reap();
-
-    if (svc->flags() & SVC_EXEC) {
-        exec_waiter_.reset();
-    }
-    if (svc->flags() & SVC_TEMPORARY) {
-        RemoveService(*svc);
-    }
-
-    return true;
-}
-
-void ServiceManager::ReapAnyOutstandingChildren() {
-    while (ReapOneProcess()) {
-    }
-}
-
-void ServiceManager::ClearExecWait() {
-    // Clear EXEC flag if there is one pending
-    // And clear the wait flag
-    for (const auto& s : services_) {
-        s->UnSetExec();
-    }
-    exec_waiter_.reset();
-}
-
-bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line, std::string* err) {
+Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
+                                            const std::string& filename, int line) {
     if (args.size() < 3) {
-        *err = "services must have a name and a program";
-        return false;
+        return Error() << "services must have a name and a program";
     }
 
     const std::string& name = args[1];
     if (!IsValidName(name)) {
-        *err = StringPrintf("invalid service name '%s'", name.c_str());
-        return false;
+        return Error() << "invalid service name '" << name << "'";
     }
 
-    Service* old_service = service_manager_->FindServiceByName(name);
+    Service* old_service = service_list_->FindService(name);
     if (old_service) {
-        *err = "ignored duplicate definition of service '" + name + "'";
-        return false;
+        return Error() << "ignored duplicate definition of service '" << name << "'";
+    }
+
+    Subcontext* restart_action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+                restart_action_subcontext = &subcontext;
+                break;
+            }
+        }
     }
 
     std::vector<std::string> str_args(args.begin() + 2, args.end());
-    service_ = std::make_unique<Service>(name, str_args);
-    return true;
+    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
+    return Success();
 }
 
-bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    return service_ ? service_->ParseLine(std::move(args), err) : false;
+Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return service_ ? service_->ParseLine(std::move(args)) : Success();
 }
 
 void ServiceParser::EndSection() {
     if (service_) {
-        service_manager_->AddService(std::move(service_));
+        service_list_->AddService(std::move(service_));
     }
 }
 
@@ -1192,3 +1088,6 @@
     // the service name to the "ctl.start" and "ctl.stop" properties.)
     return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/service.h b/init/service.h
index 25d1975..89dd780 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,6 +17,7 @@
 #ifndef _INIT_SERVICE_H
 #define _INIT_SERVICE_H
 
+#include <sys/resource.h>
 #include <sys/types.h>
 
 #include <memory>
@@ -30,9 +31,9 @@
 #include "action.h"
 #include "capabilities.h"
 #include "descriptors.h"
-#include "init_parser.h"
 #include "keyword_map.h"
-#include "util.h"
+#include "parser.h"
+#include "subcontext.h"
 
 #define SVC_DISABLED 0x001        // do not autostart with class
 #define SVC_ONESHOT 0x002         // do not restart on exit
@@ -55,46 +56,47 @@
 
 #define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
 
-class Action;
-class ServiceManager;
-
-struct ServiceEnvironmentInfo {
-    ServiceEnvironmentInfo();
-    ServiceEnvironmentInfo(const std::string& name, const std::string& value);
-    std::string name;
-    std::string value;
-};
+namespace android {
+namespace init {
 
 class Service {
   public:
-    Service(const std::string& name, const std::vector<std::string>& args);
+    Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+            const std::vector<std::string>& args);
 
     Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
             const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
             unsigned namespace_flags, const std::string& seclabel,
-            const std::vector<std::string>& args);
+            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
+
+    static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
-    bool ParseLine(const std::vector<std::string>& args, std::string* err);
-    bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
-    bool Start();
-    bool StartIfNotDisabled();
-    bool Enable();
+    Result<Success> ParseLine(const std::vector<std::string>& args);
+    Result<Success> ExecStart();
+    Result<Success> Start();
+    Result<Success> StartIfNotDisabled();
+    Result<Success> Enable();
     void Reset();
     void Stop();
     void Terminate();
     void Restart();
-    void RestartIfNeeded(time_t* process_needs_restart_at);
     void Reap();
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
     bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
-    void UnSetExec() { flags_ &= ~SVC_EXEC; }
+    void UnSetExec() {
+        is_exec_service_running_ = false;
+        flags_ &= ~SVC_EXEC;
+    }
+
+    static bool is_exec_service_running() { return is_exec_service_running_; }
 
     const std::string& name() const { return name_; }
     const std::set<std::string>& classnames() const { return classnames_; }
     unsigned flags() const { return flags_; }
     pid_t pid() const { return pid_; }
+    android::base::boot_clock::time_point time_started() const { return time_started_; }
     int crash_count() const { return crash_count_; }
     uid_t uid() const { return uid_; }
     gid_t gid() const { return gid_; }
@@ -109,11 +111,11 @@
     int priority() const { return priority_; }
     int oom_score_adjust() const { return oom_score_adjust_; }
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
+    unsigned long start_order() const { return start_order_; }
     const std::vector<std::string>& args() const { return args_; }
 
   private:
-    using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
-                                            std::string* err);
+    using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
     class OptionParserMap;
 
     void NotifyStateChange(const std::string& new_state) const;
@@ -123,29 +125,36 @@
     void KillProcessGroup(int signal);
     void SetProcessAttributes();
 
-    bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
-    bool ParseClass(const std::vector<std::string>& args, std::string* err);
-    bool ParseConsole(const std::vector<std::string>& args, std::string* err);
-    bool ParseCritical(const std::vector<std::string>& args, std::string* err);
-    bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
-    bool ParseGroup(const std::vector<std::string>& args, std::string* err);
-    bool ParsePriority(const std::vector<std::string>& args, std::string* err);
-    bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
-    bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
-    bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
-    bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
-    bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
-    bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
-    bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
-    bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
-    bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
-    bool ParseSocket(const std::vector<std::string>& args, std::string* err);
-    bool ParseFile(const std::vector<std::string>& args, std::string* err);
-    bool ParseUser(const std::vector<std::string>& args, std::string* err);
-    bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+    Result<Success> ParseCapabilities(const std::vector<std::string>& args);
+    Result<Success> ParseClass(const std::vector<std::string>& args);
+    Result<Success> ParseConsole(const std::vector<std::string>& args);
+    Result<Success> ParseCritical(const std::vector<std::string>& args);
+    Result<Success> ParseDisabled(const std::vector<std::string>& args);
+    Result<Success> ParseGroup(const std::vector<std::string>& args);
+    Result<Success> ParsePriority(const std::vector<std::string>& args);
+    Result<Success> ParseIoprio(const std::vector<std::string>& args);
+    Result<Success> ParseKeycodes(const std::vector<std::string>& args);
+    Result<Success> ParseOneshot(const std::vector<std::string>& args);
+    Result<Success> ParseOnrestart(const std::vector<std::string>& args);
+    Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
+    Result<Success> ParseNamespace(const std::vector<std::string>& args);
+    Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
+    Result<Success> ParseSeclabel(const std::vector<std::string>& args);
+    Result<Success> ParseSetenv(const std::vector<std::string>& args);
+    Result<Success> ParseShutdown(const std::vector<std::string>& args);
+    Result<Success> ParseSocket(const std::vector<std::string>& args);
+    Result<Success> ParseFile(const std::vector<std::string>& args);
+    Result<Success> ParseUser(const std::vector<std::string>& args);
+    Result<Success> ParseWritepid(const std::vector<std::string>& args);
 
     template <typename T>
-    bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+    Result<Success> AddDescriptor(const std::vector<std::string>& args);
+
+    static unsigned long next_start_order_;
+    static bool is_exec_service_running_;
 
     std::string name_;
     std::set<std::string> classnames_;
@@ -166,7 +175,7 @@
     std::string seclabel_;
 
     std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
-    std::vector<ServiceEnvironmentInfo> envvars_;
+    std::vector<std::pair<std::string, std::string>> environment_vars_;
 
     Action onrestart_;  // Commands to execute on restart.
 
@@ -182,61 +191,70 @@
 
     int oom_score_adjust_;
 
+    int swappiness_;
+    int soft_limit_in_bytes_;
+    int limit_in_bytes_;
+
     bool process_cgroup_empty_ = false;
 
+    unsigned long start_order_;
+
+    std::vector<std::pair<int, rlimit>> rlimits_;
+
     std::vector<std::string> args_;
 };
 
-class ServiceManager {
+class ServiceList {
   public:
-    static ServiceManager& GetInstance();
+    static ServiceList& GetInstance();
 
     // Exposed for testing
-    ServiceManager();
+    ServiceList();
 
     void AddService(std::unique_ptr<Service> service);
-    Service* MakeExecOneshotService(const std::vector<std::string>& args);
-    bool Exec(const std::vector<std::string>& args);
-    bool ExecStart(const std::string& name);
-    bool IsWaitingForExec() const;
-    Service* FindServiceByName(const std::string& name) const;
-    Service* FindServiceByPid(pid_t pid) const;
-    Service* FindServiceByKeychord(int keychord_id) const;
-    void ForEachService(const std::function<void(Service*)>& callback) const;
-    void ForEachServiceInClass(const std::string& classname,
-                               void (*func)(Service* svc)) const;
-    void ForEachServiceWithFlags(unsigned matchflags,
-                             void (*func)(Service* svc)) const;
-    void ReapAnyOutstandingChildren();
     void RemoveService(const Service& svc);
+
+    template <typename T, typename F = decltype(&Service::name)>
+    Service* FindService(T value, F function = &Service::name) const {
+        auto svc = std::find_if(services_.begin(), services_.end(),
+                                [&function, &value](const std::unique_ptr<Service>& s) {
+                                    return std::invoke(function, s) == value;
+                                });
+        if (svc != services_.end()) {
+            return svc->get();
+        }
+        return nullptr;
+    }
+
     void DumpState() const;
-    void ClearExecWait();
+
+    auto begin() const { return services_.begin(); }
+    auto end() const { return services_.end(); }
+    const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
+    const std::vector<Service*> services_in_shutdown_order() const;
 
   private:
-    // Cleans up a child process that exited.
-    // Returns true iff a children was cleaned up.
-    bool ReapOneProcess();
-
-    static int exec_count_; // Every service needs a unique name.
-    std::unique_ptr<Timer> exec_waiter_;
-
     std::vector<std::unique_ptr<Service>> services_;
 };
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser(ServiceManager* service_manager)
-        : service_manager_(service_manager), service_(nullptr) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+    ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
+        : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
     void EndSection() override;
 
   private:
     bool IsValidName(const std::string& name) const;
 
-    ServiceManager* service_manager_;
+    ServiceList* service_list_;
+    std::vector<Subcontext>* subcontexts_;
     std::unique_ptr<Service> service_;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b9c4627..b43c2e9 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -23,6 +23,11 @@
 
 #include <gtest/gtest.h>
 
+#include "util.h"
+
+namespace android {
+namespace init {
+
 TEST(service, pod_initialized) {
     constexpr auto memory_size = sizeof(Service);
     alignas(alignof(Service)) char old_memory[memory_size];
@@ -32,7 +37,8 @@
     }
 
     std::vector<std::string> dummy_args{"/bin/test"};
-    Service* service_in_old_memory = new (old_memory) Service("test_old_memory", dummy_args);
+    Service* service_in_old_memory =
+        new (old_memory) Service("test_old_memory", nullptr, dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory->flags());
     EXPECT_EQ(0, service_in_old_memory->pid());
@@ -51,8 +57,8 @@
         old_memory[i] = 0xFF;
     }
 
-    Service* service_in_old_memory2 = new (old_memory)
-        Service("test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", dummy_args);
+    Service* service_in_old_memory2 = new (old_memory) Service(
+        "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", nullptr, dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory2->flags());
     EXPECT_EQ(0, service_in_old_memory2->pid());
@@ -67,3 +73,121 @@
     EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
     EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 }
+
+TEST(service, make_temporary_oneshot_service_invalid_syntax) {
+    std::vector<std::string> args;
+    // Nothing.
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+    // No arguments to 'exec'.
+    args.push_back("exec");
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+    // No command in "exec --".
+    args.push_back("--");
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
+    std::vector<std::string> args;
+    args.push_back("exec");
+    args.push_back("seclabel");
+    args.push_back("root");  // uid.
+    args.push_back("root");  // gid.
+    for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
+        args.push_back("root");  // Supplementary gid.
+    }
+    args.push_back("--");
+    args.push_back("/system/bin/id");
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
+                                                bool supplementary_gids) {
+    std::vector<std::string> args;
+    args.push_back("exec");
+    if (seclabel) {
+        args.push_back("u:r:su:s0");  // seclabel
+        if (uid) {
+            args.push_back("log");  // uid
+            if (gid) {
+                args.push_back("shell");  // gid
+                if (supplementary_gids) {
+                    args.push_back("system");  // supplementary gid 0
+                    args.push_back("adb");     // supplementary gid 1
+                }
+            }
+        }
+    }
+    if (dash_dash) {
+        args.push_back("--");
+    }
+    args.push_back("/system/bin/toybox");
+    args.push_back("id");
+    auto svc = Service::MakeTemporaryOneshotService(args);
+    ASSERT_NE(nullptr, svc);
+
+    if (seclabel) {
+        ASSERT_EQ("u:r:su:s0", svc->seclabel());
+    } else {
+        ASSERT_EQ("", svc->seclabel());
+    }
+    if (uid) {
+        auto decoded_uid = DecodeUid("log");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->uid());
+    } else {
+        ASSERT_EQ(0U, svc->uid());
+    }
+    if (gid) {
+        auto decoded_uid = DecodeUid("shell");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->gid());
+    } else {
+        ASSERT_EQ(0U, svc->gid());
+    }
+    if (supplementary_gids) {
+        ASSERT_EQ(2U, svc->supp_gids().size());
+
+        auto decoded_uid = DecodeUid("system");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
+
+        decoded_uid = DecodeUid("adb");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
+    } else {
+        ASSERT_EQ(0U, svc->supp_gids().size());
+    }
+
+    ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
+    ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
+    ASSERT_EQ("id", svc->args()[1]);
+}
+
+TEST(service, make_temporary_oneshot_service_with_everything) {
+    Test_make_temporary_oneshot_service(true, true, true, true, true);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {
+    Test_make_temporary_oneshot_service(true, true, true, true, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid) {
+    Test_make_temporary_oneshot_service(true, true, true, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel) {
+    Test_make_temporary_oneshot_service(true, true, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command) {
+    Test_make_temporary_oneshot_service(true, false, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {
+    Test_make_temporary_oneshot_service(false, false, false, false, false);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
new file mode 100644
index 0000000..072a0fb
--- /dev/null
+++ b/init/sigchld_handler.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 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 "sigchld_handler.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+
+#include "init.h"
+#include "property_service.h"
+#include "service.h"
+
+using android::base::StringPrintf;
+using android::base::boot_clock;
+using android::base::make_scope_guard;
+
+namespace android {
+namespace init {
+
+static int signal_write_fd = -1;
+static int signal_read_fd = -1;
+
+static bool ReapOneProcess() {
+    siginfo_t siginfo = {};
+    // This returns a zombie pid or informs us that there are no zombies left to be reaped.
+    // It does NOT reap the pid; that is done below.
+    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
+        PLOG(ERROR) << "waitid failed";
+        return false;
+    }
+
+    auto pid = siginfo.si_pid;
+    if (pid == 0) return false;
+
+    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
+    // whenever the function returns from this point forward.
+    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
+    // want the pid to remain valid throughout that (and potentially future) usages.
+    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
+
+    std::string name;
+    std::string wait_string;
+    Service* service = nullptr;
+
+    if (PropertyChildReap(pid)) {
+        name = "Async property child";
+    } else if (SubcontextChildReap(pid)) {
+        name = "Subcontext";
+    } else {
+        service = ServiceList::GetInstance().FindService(pid, &Service::pid);
+
+        if (service) {
+            name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
+            if (service->flags() & SVC_EXEC) {
+                auto exec_duration = boot_clock::now() - service->time_started();
+                auto exec_duration_ms =
+                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
+                wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+            }
+        } else {
+            name = StringPrintf("Untracked pid %d", pid);
+        }
+    }
+
+    auto status = siginfo.si_status;
+    if (WIFEXITED(status)) {
+        LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
+    } else if (WIFSIGNALED(status)) {
+        LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
+    }
+
+    if (!service) return true;
+
+    service->Reap();
+
+    if (service->flags() & SVC_TEMPORARY) {
+        ServiceList::GetInstance().RemoveService(*service);
+    }
+
+    return true;
+}
+
+static void handle_signal() {
+    // Clear outstanding requests.
+    char buf[32];
+    read(signal_read_fd, buf, sizeof(buf));
+
+    ReapAnyOutstandingChildren();
+}
+
+static void SIGCHLD_handler(int) {
+    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
+        PLOG(ERROR) << "write(signal_write_fd) failed";
+    }
+}
+
+void ReapAnyOutstandingChildren() {
+    while (ReapOneProcess()) {
+    }
+}
+
+void sigchld_handler_init() {
+    // Create a signalling mechanism for SIGCHLD.
+    int s[2];
+    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
+        PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
+    }
+
+    signal_write_fd = s[0];
+    signal_read_fd = s[1];
+
+    // Write to signal_write_fd if we catch SIGCHLD.
+    struct sigaction act;
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = SIGCHLD_handler;
+    act.sa_flags = SA_NOCLDSTOP;
+    sigaction(SIGCHLD, &act, 0);
+
+    ReapAnyOutstandingChildren();
+
+    register_epoll_handler(signal_read_fd, handle_signal);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/signal_handler.h b/init/sigchld_handler.h
similarity index 74%
rename from init/signal_handler.h
rename to init/sigchld_handler.h
index 449b4af..c86dc8d 100644
--- a/init/signal_handler.h
+++ b/init/sigchld_handler.h
@@ -14,9 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_SIGCHLD_HANDLER_H_
+#define _INIT_SIGCHLD_HANDLER_H_
 
-void signal_handler_init(void);
+namespace android {
+namespace init {
+
+void ReapAnyOutstandingChildren();
+
+void sigchld_handler_init(void);
+
+}  // namespace init
+}  // namespace android
 
 #endif
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
deleted file mode 100644
index 4d56d84..0000000
--- a/init/signal_handler.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2010 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 <signal.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-
-#include "init.h"
-#include "service.h"
-
-static int signal_write_fd = -1;
-static int signal_read_fd = -1;
-
-static void handle_signal() {
-    // Clear outstanding requests.
-    char buf[32];
-    read(signal_read_fd, buf, sizeof(buf));
-
-    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-}
-
-static void SIGCHLD_handler(int) {
-    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
-        PLOG(ERROR) << "write(signal_write_fd) failed";
-    }
-}
-
-void signal_handler_init() {
-    // Create a signalling mechanism for SIGCHLD.
-    int s[2];
-    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
-        PLOG(ERROR) << "socketpair failed";
-        exit(1);
-    }
-
-    signal_write_fd = s[0];
-    signal_read_fd = s[1];
-
-    // Write to signal_write_fd if we catch SIGCHLD.
-    struct sigaction act;
-    memset(&act, 0, sizeof(act));
-    act.sa_handler = SIGCHLD_handler;
-    act.sa_flags = SA_NOCLDSTOP;
-    sigaction(SIGCHLD, &act, 0);
-
-    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-
-    register_epoll_handler(signal_read_fd, handle_signal);
-}
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
new file mode 100644
index 0000000..927953d
--- /dev/null
+++ b/init/subcontext.cpp
@@ -0,0 +1,285 @@
+/*
+ * 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 "subcontext.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <selinux/android.h>
+
+#include "action.h"
+#include "system/core/init/subcontext.pb.h"
+#include "util.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetExecutablePath;
+using android::base::Join;
+using android::base::Socketpair;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+const std::string kInitContext = "u:r:init:s0";
+const std::string kVendorContext = "u:r:vendor_init:s0";
+
+namespace {
+
+constexpr size_t kBufferSize = 4096;
+
+Result<std::string> ReadMessage(int socket) {
+    char buffer[kBufferSize] = {};
+    auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
+    if (result <= 0) {
+        return ErrnoError();
+    }
+    return std::string(buffer, result);
+}
+
+template <typename T>
+Result<Success> SendMessage(int socket, const T& message) {
+    std::string message_string;
+    if (!message.SerializeToString(&message_string)) {
+        return Error() << "Unable to serialize message";
+    }
+
+    if (message_string.size() > kBufferSize) {
+        return Error() << "Serialized message too long to send";
+    }
+
+    if (auto result =
+            TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
+        result != static_cast<long>(message_string.size())) {
+        return ErrnoError() << "send() failed to send message contents";
+    }
+    return Success();
+}
+
+class SubcontextProcess {
+  public:
+    SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd)
+        : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
+    void MainLoop();
+
+  private:
+    void RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+                    SubcontextReply::ResultMessage* result_message) const;
+
+    const KeywordFunctionMap* function_map_;
+    const std::string context_;
+    const int init_fd_;
+};
+
+void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+                                   SubcontextReply::ResultMessage* result_message) const {
+    // Need to use ArraySplice instead of this code.
+    auto args = std::vector<std::string>();
+    for (const auto& string : execute_command.args()) {
+        args.emplace_back(string);
+    }
+
+    auto map_result = function_map_->FindFunction(args);
+    Result<Success> result;
+    if (!map_result) {
+        result = Error() << "Cannot find command: " << map_result.error();
+    } else {
+        result = RunBuiltinFunction(map_result->second, args, context_);
+    }
+
+    if (result) {
+        result_message->set_success(true);
+    } else {
+        result_message->set_success(false);
+        result_message->set_error_string(result.error_string());
+        result_message->set_error_errno(result.error_errno());
+    }
+}
+
+void SubcontextProcess::MainLoop() {
+    pollfd ufd[1];
+    ufd[0].events = POLLIN;
+    ufd[0].fd = init_fd_;
+
+    while (true) {
+        ufd[0].revents = 0;
+        int nr = TEMP_FAILURE_RETRY(poll(ufd, arraysize(ufd), -1));
+        if (nr == 0) continue;
+        if (nr < 0) {
+            PLOG(FATAL) << "poll() of subcontext socket failed, continuing";
+        }
+
+        auto init_message = ReadMessage(init_fd_);
+        if (!init_message) {
+            LOG(FATAL) << "Could not read message from init: " << init_message.error();
+        }
+
+        auto subcontext_command = SubcontextCommand();
+        if (!subcontext_command.ParseFromString(*init_message)) {
+            LOG(FATAL) << "Unable to parse message from init";
+        }
+
+        auto reply = SubcontextReply();
+        switch (subcontext_command.command_case()) {
+            case SubcontextCommand::kExecuteCommand: {
+                RunCommand(subcontext_command.execute_command(), reply.mutable_result());
+                break;
+            }
+            default:
+                LOG(FATAL) << "Unknown message type from init: "
+                           << subcontext_command.command_case();
+        }
+
+        if (auto result = SendMessage(init_fd_, reply); !result) {
+            LOG(FATAL) << "Failed to send message to init: " << result.error();
+        }
+    }
+}
+
+}  // namespace
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) {
+    if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
+
+    auto context = std::string(argv[2]);
+    auto init_fd = std::atoi(argv[3]);
+
+    auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
+    subcontext_process.MainLoop();
+    return 0;
+}
+
+void Subcontext::Fork() {
+    unique_fd subcontext_socket;
+    if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {
+        LOG(FATAL) << "Could not create socket pair to communicate to subcontext";
+        return;
+    }
+
+    auto result = fork();
+
+    if (result == -1) {
+        LOG(FATAL) << "Could not fork subcontext";
+    } else if (result == 0) {
+        socket_.reset();
+
+        // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
+        // in the subcontext process after we exec.
+        int child_fd = dup(subcontext_socket);
+        if (child_fd < 0) {
+            PLOG(FATAL) << "Could not dup child_fd";
+        }
+
+        if (setexeccon(context_.c_str()) < 0) {
+            PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+        }
+
+        auto init_path = GetExecutablePath();
+        auto child_fd_string = std::to_string(child_fd);
+        const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
+                              child_fd_string.c_str(), nullptr};
+        execv(init_path.data(), const_cast<char**>(args));
+
+        PLOG(FATAL) << "Could not execv subcontext init";
+    } else {
+        subcontext_socket.reset();
+        pid_ = result;
+        LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_;
+    }
+}
+
+void Subcontext::Restart() {
+    LOG(ERROR) << "Restarting subcontext '" << context_ << "'";
+    if (pid_) {
+        kill(pid_, SIGKILL);
+    }
+    pid_ = 0;
+    socket_.reset();
+    Fork();
+}
+
+Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+    auto subcontext_command = SubcontextCommand();
+    std::copy(
+        args.begin(), args.end(),
+        RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
+
+    if (auto result = SendMessage(socket_, subcontext_command); !result) {
+        Restart();
+        return ErrnoError() << "Failed to send message to subcontext";
+    }
+
+    auto subcontext_message = ReadMessage(socket_);
+    if (!subcontext_message) {
+        Restart();
+        return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
+    }
+
+    auto subcontext_reply = SubcontextReply();
+    if (!subcontext_reply.ParseFromString(*subcontext_message)) {
+        Restart();
+        return Error() << "Unable to parse message from subcontext";
+    }
+
+    switch (subcontext_reply.reply_case()) {
+        case SubcontextReply::kResult: {
+            auto result = subcontext_reply.result();
+            if (result.success()) {
+                return Success();
+            } else {
+                return ResultError(result.error_string(), result.error_errno());
+            }
+        }
+        default:
+            return Error() << "Unknown message type from subcontext: "
+                           << subcontext_reply.reply_case();
+    }
+}
+
+static std::vector<Subcontext> subcontexts;
+
+std::vector<Subcontext>* InitializeSubcontexts() {
+    if (GetBoolProperty("ro.init.subcontexts_enabled", false)) {
+        static const char* const paths_and_secontexts[][2] = {
+            {"/vendor", kVendorContext.c_str()},
+        };
+        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+            subcontexts.emplace_back(path_prefix, secontext);
+        }
+    }
+    return &subcontexts;
+}
+
+bool SubcontextChildReap(pid_t pid) {
+    for (auto& subcontext : subcontexts) {
+        if (subcontext.pid() == pid) {
+            subcontext.Restart();
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/subcontext.h b/init/subcontext.h
new file mode 100644
index 0000000..e920034
--- /dev/null
+++ b/init/subcontext.h
@@ -0,0 +1,80 @@
+/*
+ * 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_SUBCONTEXT_H
+#define _INIT_SUBCONTEXT_H
+
+#include <signal.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "builtins.h"
+
+namespace android {
+namespace init {
+
+extern const std::string kInitContext;
+extern const std::string kVendorContext;
+
+class Subcontext {
+  public:
+    Subcontext(std::string path_prefix, std::string context)
+        : path_prefix_(std::move(path_prefix)), context_(std::move(context)) {
+        Fork();
+    }
+
+    Result<Success> Execute(const std::vector<std::string>& command);
+    void Restart();
+
+    const std::string& path_prefix() const { return path_prefix_; }
+    const std::string& context() const { return context_; }
+    pid_t pid() const { return pid_; }
+
+  private:
+    void Fork();
+
+    std::string path_prefix_;
+    std::string context_;
+    pid_t pid_;
+    android::base::unique_fd socket_;
+};
+
+// For testing, to kill the subcontext after the test has completed.
+class SubcontextKiller {
+  public:
+    SubcontextKiller(const Subcontext& subcontext) : subcontext_(subcontext) {}
+    ~SubcontextKiller() {
+        if (subcontext_.pid() > 0) {
+            kill(subcontext_.pid(), SIGTERM);
+            kill(subcontext_.pid(), SIGKILL);
+        }
+    }
+
+  private:
+    const Subcontext& subcontext_;
+};
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
+std::vector<Subcontext>* InitializeSubcontexts();
+bool SubcontextChildReap(pid_t pid);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/subcontext.proto b/init/subcontext.proto
new file mode 100644
index 0000000..0d89734
--- /dev/null
+++ b/init/subcontext.proto
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message SubcontextCommand {
+    message ExecuteCommand { repeated string args = 1; }
+    oneof command { ExecuteCommand execute_command = 1; }
+}
+
+message SubcontextReply {
+    message ResultMessage {
+        optional bool success = 1;
+        optional string error_string = 2;
+        optional int32 error_errno = 3;
+    }
+
+    oneof reply { ResultMessage result = 1; }
+}
\ No newline at end of file
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
new file mode 100644
index 0000000..a62b959
--- /dev/null
+++ b/init/subcontext_benchmark.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 "subcontext.h"
+
+#include <benchmark/benchmark.h>
+
+#include "test_function_map.h"
+
+namespace android {
+namespace init {
+
+static void BenchmarkSuccess(benchmark::State& state) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    while (state.KeepRunning()) {
+        subcontext.Execute(std::vector<std::string>{"return_success"});
+    }
+}
+
+BENCHMARK(BenchmarkSuccess);
+
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
+    test_function_map.Add("return_success", 0, 0, true,
+                          [](const BuiltinArguments& args) { return Success(); });
+
+    return test_function_map;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+        auto test_function_map = android::init::BuildTestFunctionMap();
+        return android::init::SubcontextMain(argc, argv, &test_function_map);
+    }
+
+    ::benchmark::Initialize(&argc, argv);
+    if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
+    ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
new file mode 100644
index 0000000..60b45b9
--- /dev/null
+++ b/init/subcontext_test.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 "subcontext.h"
+
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "builtin_arguments.h"
+#include "test_function_map.h"
+
+using namespace std::literals;
+
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::WaitForProperty;
+
+namespace android {
+namespace init {
+
+TEST(subcontext, CheckDifferentPid) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
+    ASSERT_FALSE(result);
+
+    auto pids = Split(result.error_string(), " ");
+    ASSERT_EQ(2U, pids.size());
+    auto our_pid = std::to_string(getpid());
+    EXPECT_NE(our_pid, pids[0]);
+    EXPECT_EQ(our_pid, pids[1]);
+}
+
+TEST(subcontext, SetProp) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    SetProperty("init.test.subcontext", "fail");
+    WaitForProperty("init.test.subcontext", "fail");
+
+    auto args = std::vector<std::string>{
+        "setprop",
+        "init.test.subcontext",
+        "success",
+    };
+    auto result = subcontext.Execute(args);
+    ASSERT_TRUE(result) << result.error();
+
+    EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s));
+}
+
+TEST(subcontext, MultipleCommands) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto first_pid = subcontext.pid();
+
+    auto expected_words = std::vector<std::string>{
+        "this",
+        "is",
+        "a",
+        "test",
+    };
+
+    for (const auto& word : expected_words) {
+        auto args = std::vector<std::string>{
+            "add_word",
+            word,
+        };
+        auto result = subcontext.Execute(args);
+        ASSERT_TRUE(result) << result.error();
+    }
+
+    auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
+    ASSERT_FALSE(result);
+    EXPECT_EQ(Join(expected_words, " "), result.error_string());
+    EXPECT_EQ(first_pid, subcontext.pid());
+}
+
+TEST(subcontext, RecoverAfterAbort) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto first_pid = subcontext.pid();
+
+    auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
+    ASSERT_FALSE(result);
+
+    auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
+    ASSERT_FALSE(result2);
+    EXPECT_EQ("Sane error!", result2.error_string());
+    EXPECT_NE(subcontext.pid(), first_pid);
+}
+
+TEST(subcontext, ContextString) {
+    auto subcontext = Subcontext("path", kVendorContext);
+    auto subcontext_killer = SubcontextKiller(subcontext);
+
+    auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
+    ASSERT_FALSE(result);
+    ASSERT_EQ(kVendorContext, result.error_string());
+}
+
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
+    // For CheckDifferentPid
+    test_function_map.Add("return_pids_as_error", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << getpid() << " " << getppid();
+                          });
+
+    // For SetProp
+    test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
+        android::base::SetProperty(args[1], args[2]);
+        return Success();
+    });
+
+    // For MultipleCommands
+    // Using a shared_ptr to extend lifetime of words to both lambdas
+    auto words = std::make_shared<std::vector<std::string>>();
+    test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
+        words->emplace_back(args[1]);
+        return Success();
+    });
+    test_function_map.Add("return_words_as_error", 0, 0, true,
+                          [words](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << Join(*words, " ");
+                          });
+
+    // For RecoverAfterAbort
+    test_function_map.Add("cause_log_fatal", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << std::string(4097, 'f');
+                          });
+    test_function_map.Add(
+        "generate_sane_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
+
+    // For ContextString
+    test_function_map.Add(
+        "return_context_as_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << args.context; });
+
+    return test_function_map;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+        auto test_function_map = android::init::BuildTestFunctionMap();
+        return android::init::SubcontextMain(argc, argv, &test_function_map);
+    }
+
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/init/test_function_map.h b/init/test_function_map.h
new file mode 100644
index 0000000..583df1a
--- /dev/null
+++ b/init/test_function_map.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 _INIT_TEST_FUNCTION_MAP_H
+#define _INIT_TEST_FUNCTION_MAP_H
+
+#include <string>
+#include <vector>
+
+#include "builtin_arguments.h"
+#include "keyword_map.h"
+
+namespace android {
+namespace init {
+
+class TestFunctionMap : public KeywordFunctionMap {
+  public:
+    // Helper for argument-less functions
+    using BuiltinFunctionNoArgs = std::function<void(void)>;
+    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+        Add(name, 0, 0, false, [function](const BuiltinArguments&) {
+            function();
+            return Success();
+        });
+    }
+
+    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+             bool run_in_subcontext, const BuiltinFunction function) {
+        builtin_functions_[name] =
+            make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
+    }
+
+  private:
+    Map builtin_functions_ = {};
+
+    const Map& map() const override { return builtin_functions_; }
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
new file mode 100644
index 0000000..f8d9b6b
--- /dev/null
+++ b/init/tokenizer.cpp
@@ -0,0 +1,124 @@
+#include "tokenizer.h"
+
+namespace android {
+namespace init {
+
+int next_token(struct parse_state *state)
+{
+    char *x = state->ptr;
+    char *s;
+
+    if (state->nexttoken) {
+        int t = state->nexttoken;
+        state->nexttoken = 0;
+        return t;
+    }
+
+    for (;;) {
+        switch (*x) {
+        case 0:
+            state->ptr = x;
+            return T_EOF;
+        case '\n':
+            x++;
+            state->ptr = x;
+            return T_NEWLINE;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            continue;
+        case '#':
+            while (*x && (*x != '\n')) x++;
+            if (*x == '\n') {
+                state->ptr = x+1;
+                return T_NEWLINE;
+            } else {
+                state->ptr = x;
+                return T_EOF;
+            }
+        default:
+            goto text;
+        }
+    }
+
+textdone:
+    state->ptr = x;
+    *s = 0;
+    return T_TEXT;
+text:
+    state->text = s = x;
+textresume:
+    for (;;) {
+        switch (*x) {
+        case 0:
+            goto textdone;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            goto textdone;
+        case '\n':
+            state->nexttoken = T_NEWLINE;
+            x++;
+            goto textdone;
+        case '"':
+            x++;
+            for (;;) {
+                switch (*x) {
+                case 0:
+                        /* unterminated quoted thing */
+                    state->ptr = x;
+                    return T_EOF;
+                case '"':
+                    x++;
+                    goto textresume;
+                default:
+                    *s++ = *x++;
+                }
+            }
+            break;
+        case '\\':
+            x++;
+            switch (*x) {
+            case 0:
+                goto textdone;
+            case 'n':
+                *s++ = '\n';
+                break;
+            case 'r':
+                *s++ = '\r';
+                break;
+            case 't':
+                *s++ = '\t';
+                break;
+            case '\\':
+                *s++ = '\\';
+                break;
+            case '\r':
+                    /* \ <cr> <lf> -> line continuation */
+                if (x[1] != '\n') {
+                    x++;
+                    continue;
+                }
+            case '\n':
+                    /* \ <lf> -> line continuation */
+                state->line++;
+                x++;
+                    /* eat any extra whitespace */
+                while((*x == ' ') || (*x == '\t')) x++;
+                continue;
+            default:
+                    /* unknown escape -- just copy */
+                *s++ = *x++;
+            }
+            continue;
+        default:
+            *s++ = *x++;
+        }
+    }
+    return T_EOF;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/signal_handler.h b/init/tokenizer.h
similarity index 65%
copy from init/signal_handler.h
copy to init/tokenizer.h
index 449b4af..72c08ef 100644
--- a/init/signal_handler.h
+++ b/init/tokenizer.h
@@ -14,9 +14,27 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_TOKENIZER_H_
+#define _INIT_TOKENIZER_H_
 
-void signal_handler_init(void);
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
+
+namespace android {
+namespace init {
+
+struct parse_state
+{
+    char *ptr;
+    char *text;
+    int line;
+    int nexttoken;
+};
+
+int next_token(struct parse_state *state);
+
+}  // namespace init
+}  // namespace android
 
 #endif
diff --git a/init/uevent.h b/init/uevent.h
index 1095665..c4fd945 100644
--- a/init/uevent.h
+++ b/init/uevent.h
@@ -19,6 +19,9 @@
 
 #include <string>
 
+namespace android {
+namespace init {
+
 struct Uevent {
     std::string action;
     std::string path;
@@ -31,4 +34,7 @@
     int minor;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 923fa8e..ac1d7c7 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -26,6 +26,9 @@
 #include <android-base/logging.h>
 #include <cutils/uevent.h>
 
+namespace android {
+namespace init {
+
 static void ParseEvent(const char* msg, Uevent* uevent) {
     uevent->partition_num = -1;
     uevent->major = -1;
@@ -165,7 +168,7 @@
     return RegenerateUeventsForDir(d.get(), callback);
 }
 
-const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
 
 void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
     for (const auto path : kRegenerationPaths) {
@@ -212,3 +215,6 @@
         }
     }
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index 1964688..5b453fe 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -29,6 +29,9 @@
 
 #define UEVENT_MSG_LEN 2048
 
+namespace android {
+namespace init {
+
 enum class ListenerAction {
     kStop = 0,  // Stop regenerating uevents as we've handled the one(s) we're interested in.
     kContinue,  // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
@@ -36,8 +39,6 @@
 
 using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
 
-extern const char* kRegenerationPaths[3];
-
 class UeventListener {
   public:
     UeventListener();
@@ -55,4 +56,7 @@
     android::base::unique_fd device_fd_;
 };
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index ff64e8e..1435d82 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -27,15 +27,16 @@
 #include <set>
 #include <thread>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
 #include "devices.h"
 #include "firmware_handler.h"
 #include "log.h"
+#include "selinux.h"
 #include "uevent_listener.h"
 #include "ueventd_parser.h"
 #include "util.h"
@@ -100,6 +101,9 @@
 // the uevent listener resumes in polling mode and will handle the uevents that occurred during
 // coldboot.
 
+namespace android {
+namespace init {
+
 class ColdBoot {
   public:
     ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
@@ -158,9 +162,7 @@
 }
 
 void ColdBoot::DoRestoreCon() {
-    for (const char* path : kRegenerationPaths) {
-        selinux_android_restorecon(path, SELINUX_ANDROID_RESTORECON_RECURSE);
-    }
+    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
     device_handler_.set_skip_restorecon(false);
 }
 
@@ -198,7 +200,7 @@
 }
 
 void ColdBoot::Run() {
-    Timer cold_boot_timer;
+    android::base::Timer cold_boot_timer;
 
     RegenerateUevents();
 
@@ -209,7 +211,7 @@
     WaitForSubProcesses();
 
     close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
-    LOG(INFO) << "Coldboot took " << cold_boot_timer;
+    LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
 }
 
 DeviceHandler CreateDeviceHandler() {
@@ -221,10 +223,10 @@
     using namespace std::placeholders;
     std::vector<SysfsPermissions> sysfs_permissions;
     std::vector<Permissions> dev_permissions;
-    parser.AddSingleLineParser(
-        "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
+    parser.AddSingleLineParser("/sys/",
+                               std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));
     parser.AddSingleLineParser("/dev/",
-                               std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+                               std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));
 
     parser.ParseConfig("/ueventd.rc");
     parser.ParseConfig("/vendor/ueventd.rc");
@@ -256,9 +258,8 @@
 
     LOG(INFO) << "ueventd started!";
 
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
+    SelinuxSetupKernelLogging();
+    SelabelInitialize();
 
     DeviceHandler device_handler = CreateDeviceHandler();
     UeventListener uevent_listener;
@@ -268,6 +269,13 @@
         cold_boot.Run();
     }
 
+    // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
+    signal(SIGCHLD, SIG_IGN);
+    // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
+    // for SIGCHLD above.
+    while (waitpid(-1, nullptr, WNOHANG) > 0) {
+    }
+
     uevent_listener.Poll([&device_handler](const Uevent& uevent) {
         HandleFirmwareEvent(uevent);
         device_handler.HandleDeviceEvent(uevent);
@@ -276,3 +284,6 @@
 
     return 0;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd.h b/init/ueventd.h
index 1f424d3..51775ec 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,6 +17,12 @@
 #ifndef _INIT_UEVENTD_H_
 #define _INIT_UEVENTD_H_
 
+namespace android {
+namespace init {
+
 int ueventd_main(int argc, char** argv);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 7156e76..cd7adb4 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -21,18 +21,19 @@
 
 #include "keyword_map.h"
 
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
-                          std::vector<SysfsPermissions>* out_sysfs_permissions,
-                          std::vector<Permissions>* out_dev_permissions) {
+namespace android {
+namespace init {
+
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
+                                     std::vector<Permissions>* out_dev_permissions) {
     bool is_sysfs = out_sysfs_permissions != nullptr;
     if (is_sysfs && args.size() != 5) {
-        *err = "/sys/ lines must have 5 entries";
-        return false;
+        return Error() << "/sys/ lines must have 5 entries";
     }
 
     if (!is_sysfs && args.size() != 4) {
-        *err = "/dev/ lines must have 4 entries";
-        return false;
+        return Error() << "/dev/ lines must have 4 entries";
     }
 
     auto it = args.begin();
@@ -46,23 +47,20 @@
     char* end_pointer = 0;
     mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
     if (end_pointer == nullptr || *end_pointer != '\0') {
-        *err = "invalid mode '" + perm_string + "'";
-        return false;
+        return Error() << "invalid mode '" << perm_string << "'";
     }
 
     std::string& uid_string = *it++;
     passwd* pwd = getpwnam(uid_string.c_str());
     if (!pwd) {
-        *err = "invalid uid '" + uid_string + "'";
-        return false;
+        return Error() << "invalid uid '" << uid_string << "'";
     }
     uid_t uid = pwd->pw_uid;
 
     std::string& gid_string = *it++;
     struct group* grp = getgrnam(gid_string.c_str());
     if (!grp) {
-        *err = "invalid gid '" + gid_string + "'";
-        return false;
+        return Error() << "invalid gid '" << gid_string << "'";
     }
     gid_t gid = grp->gr_gid;
 
@@ -71,53 +69,49 @@
     } else {
         out_dev_permissions->emplace_back(name, perm, uid, gid);
     }
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                   int line, std::string* err) {
+Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+                                              const std::string& filename, int line) {
     if (args.size() != 2) {
-        *err = "subsystems must have exactly one name";
-        return false;
+        return Error() << "subsystems must have exactly one name";
     }
 
     if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
-        *err = "ignoring duplicate subsystem entry";
-        return false;
+        return Error() << "ignoring duplicate subsystem entry";
     }
 
-    subsystem_.name_ = args[1];
+    subsystem_ = Subsystem(std::move(args[1]));
 
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
     if (args[1] == "uevent_devname") {
         subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
-        return true;
+        return Success();
     }
     if (args[1] == "uevent_devpath") {
         subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
-        return true;
+        return Success();
     }
 
-    *err = "invalid devname '" + args[1] + "'";
-    return false;
+    return Error() << "invalid devname '" << args[1] << "'";
 }
 
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
     if (args[1].front() != '/') {
-        *err = "dirname '" + args[1] + " ' does not start with '/'";
-        return false;
+        return Error() << "dirname '" << args[1] << " ' does not start with '/'";
     }
 
     subsystem_.dir_name_ = args[1];
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    using OptionParser =
-        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+
     static class OptionParserMap : public KeywordMap<OptionParser> {
       private:
         const Map& map() const override {
@@ -131,15 +125,16 @@
         }
     } parser_map;
 
-    auto parser = parser_map.FindFunction(args, err);
+    auto parser = parser_map.FindFunction(args);
 
-    if (!parser) {
-        return false;
-    }
+    if (!parser) return Error() << parser.error();
 
-    return (this->*parser)(std::move(args), err);
+    return std::invoke(*parser, this, std::move(args));
 }
 
 void SubsystemParser::EndSection() {
     subsystems_->emplace_back(std::move(subsystem_));
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index c1ce976..18d1027 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -21,26 +21,32 @@
 #include <vector>
 
 #include "devices.h"
-#include "init_parser.h"
+#include "parser.h"
+
+namespace android {
+namespace init {
 
 class SubsystemParser : public SectionParser {
   public:
     SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
     void EndSection() override;
 
   private:
-    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
-    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+    Result<Success> ParseDevName(std::vector<std::string>&& args);
+    Result<Success> ParseDirName(std::vector<std::string>&& args);
 
     Subsystem subsystem_;
     std::vector<Subsystem>* subsystems_;
 };
 
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
-                          std::vector<SysfsPermissions>* out_sysfs_permissions,
-                          std::vector<Permissions>* out_dev_permissions);
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
+                                     std::vector<Permissions>* out_dev_permissions);
+
+}  // namespace init
+}  // namespace android
 
 #endif
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
new file mode 100644
index 0000000..7290051
--- /dev/null
+++ b/init/ueventd_test.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 <linux/futex.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <chrono>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+template <typename T, typename F>
+void WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_parameters,
+                              F function) {
+    auto num_threads = files_and_parameters.size();
+    pthread_barrier_t barrier;
+    pthread_barrier_init(&barrier, nullptr, num_threads);
+    auto barrier_destroy =
+        android::base::make_scope_guard([&barrier]() { pthread_barrier_destroy(&barrier); });
+
+    auto make_thread_function = [&function, &barrier](const auto& file, const auto& parameter) {
+        return [&]() {
+            function(parameter);
+            pthread_barrier_wait(&barrier);
+            android::base::WriteStringToFile("<empty>", file);
+        };
+    };
+
+    std::vector<std::thread> threads;
+    // TODO(b/63712782): Structured bindings + templated containers are broken in clang :(
+    // for (const auto& [file, parameter] : files_and_parameters) {
+    for (const auto& pair : files_and_parameters) {
+        const auto& file = pair.first;
+        const auto& parameter = pair.second;
+        threads.emplace_back(std::thread(make_thread_function(file, parameter)));
+    }
+
+    for (auto& thread : threads) {
+        thread.join();
+    }
+}
+
+TEST(ueventd, setegid_IsPerThread) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+
+    TemporaryDir dir;
+
+    gid_t gid = 0;
+    std::vector<std::pair<std::string, gid_t>> files_and_gids;
+    std::generate_n(std::back_inserter(files_and_gids), 100, [&gid, &dir]() {
+        gid++;
+        return std::pair(dir.path + "/gid_"s + std::to_string(gid), gid);
+    });
+
+    WriteFromMultipleThreads(files_and_gids, [](gid_t gid) { EXPECT_EQ(0, setegid(gid)); });
+
+    for (const auto& [file, expected_gid] : files_and_gids) {
+        struct stat info;
+        ASSERT_EQ(0, stat(file.c_str(), &info));
+        EXPECT_EQ(expected_gid, info.st_gid);
+    }
+}
+
+TEST(ueventd, setfscreatecon_IsPerThread) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+    if (!is_selinux_enabled() || security_getenforce() == 1) {
+        GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+        return;
+    }
+
+    const char* const contexts[] = {
+        "u:object_r:audio_device:s0",
+        "u:object_r:sensors_device:s0",
+        "u:object_r:video_device:s0"
+        "u:object_r:zero_device:s0",
+    };
+
+    TemporaryDir dir;
+    std::vector<std::pair<std::string, std::string>> files_and_contexts;
+    for (const char* context : contexts) {
+        files_and_contexts.emplace_back(dir.path + "/context_"s + context, context);
+    }
+
+    WriteFromMultipleThreads(files_and_contexts, [](const std::string& context) {
+        EXPECT_EQ(0, setfscreatecon(context.c_str()));
+    });
+
+    for (const auto& [file, expected_context] : files_and_contexts) {
+        char* file_context;
+        ASSERT_GT(getfilecon(file.c_str(), &file_context), 0);
+        EXPECT_EQ(expected_context, file_context);
+        freecon(file_context);
+    }
+}
+
+TEST(ueventd, selabel_lookup_MultiThreaded) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+
+    // Test parameters
+    constexpr auto num_threads = 10;
+    constexpr auto run_time = 200ms;
+
+    std::unique_ptr<selabel_handle, decltype(&selabel_close)> sehandle(
+        selinux_android_file_context_handle(), &selabel_close);
+
+    ASSERT_TRUE(sehandle);
+
+    struct {
+        const char* file;
+        int mode;
+        std::string expected_context;
+    } files_and_modes[] = {
+        {"/dev/zero", 020666, ""},
+        {"/dev/null", 020666, ""},
+        {"/dev/random", 020666, ""},
+        {"/dev/urandom", 020666, ""},
+    };
+
+    // Precondition, ensure that we can lookup all of these from a single thread, and store the
+    // expected context for each.
+    for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+        char* secontext;
+        ASSERT_EQ(0, selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+                                    files_and_modes[i].mode));
+        files_and_modes[i].expected_context = secontext;
+        freecon(secontext);
+    }
+
+    // Now that we know we can access them, and what their context should be, run in parallel.
+    std::atomic_bool stopped = false;
+    std::atomic_uint num_api_failures = 0;
+    std::atomic_uint num_context_check_failures = 0;
+    std::atomic_uint num_successes = 0;
+
+    auto thread_function = [&]() {
+        while (!stopped) {
+            for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+                char* secontext;
+                int result = selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+                                            files_and_modes[i].mode);
+                if (result != 0) {
+                    num_api_failures++;
+                } else {
+                    if (files_and_modes[i].expected_context != secontext) {
+                        num_context_check_failures++;
+                    } else {
+                        num_successes++;
+                    }
+                    freecon(secontext);
+                }
+            }
+        }
+    };
+
+    std::vector<std::thread> threads;
+    std::generate_n(back_inserter(threads), num_threads,
+                    [&]() { return std::thread(thread_function); });
+
+    std::this_thread::sleep_for(run_time);
+    stopped = true;
+    for (auto& thread : threads) {
+        thread.join();
+    }
+
+    EXPECT_EQ(0U, num_api_failures);
+    EXPECT_EQ(0U, num_context_check_failures);
+    EXPECT_GT(num_successes, 0U);
+}
diff --git a/init/util.cpp b/init/util.cpp
index dfdfeb4..a19a6f3 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,7 @@
 #include <selinux/android.h>
 
 #include "reboot.h"
+#include "selinux.h"
 
 #ifdef _INIT_INIT_H
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -50,31 +51,26 @@
 using android::base::boot_clock;
 using namespace std::literals::string_literals;
 
-// DecodeUid() - decodes and returns the given string, which can be either the
-// numeric or name representation, into the integer uid or gid. Returns
-// UINT_MAX on error.
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
-    *uid = UINT_MAX;
-    *err = "";
+namespace android {
+namespace init {
 
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
+
+// DecodeUid() - decodes and returns the given string, which can be either the
+// numeric or name representation, into the integer uid or gid.
+Result<uid_t> DecodeUid(const std::string& name) {
     if (isalpha(name[0])) {
         passwd* pwd = getpwnam(name.c_str());
-        if (!pwd) {
-            *err = "getpwnam failed: "s + strerror(errno);
-            return false;
-        }
-        *uid = pwd->pw_uid;
-        return true;
+        if (!pwd) return ErrnoError() << "getpwnam failed";
+
+        return pwd->pw_uid;
     }
 
     errno = 0;
     uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
-    if (errno) {
-        *err = "strtoul failed: "s + strerror(errno);
-        return false;
-    }
-    *uid = result;
-    return true;
+    if (errno) return ErrnoError() << "strtoul failed";
+
+    return result;
 }
 
 /*
@@ -84,7 +80,7 @@
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon, selabel_handle* sehandle) {
+                 const char* socketcon) {
     if (socketcon) {
         if (setsockcreatecon(socketcon) == -1) {
             PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -111,11 +107,9 @@
         return -1;
     }
 
-    char *filecon = NULL;
-    if (sehandle) {
-        if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
-            setfscreatecon(filecon);
-        }
+    std::string secontext;
+    if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
     if (passcred) {
@@ -129,8 +123,9 @@
     int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
     int savederrno = errno;
 
-    setfscreatecon(NULL);
-    freecon(filecon);
+    if (!secontext.empty()) {
+        setfscreatecon(nullptr);
+    }
 
     if (ret) {
         errno = savederrno;
@@ -159,75 +154,68 @@
     return -1;
 }
 
-bool ReadFile(const std::string& path, std::string* content, std::string* err) {
-    content->clear();
-    *err = "";
-
+Result<std::string> ReadFile(const std::string& path) {
     android::base::unique_fd fd(
         TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (fd == -1) {
-        *err = "Unable to open '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "open() failed";
     }
 
     // For security reasons, disallow world-writable
     // or group-writable files.
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
-        *err = "fstat failed for '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "fstat failed()";
     }
     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
-        *err = "Skipping insecure file '" + path + "'";
-        return false;
+        return Error() << "Skipping insecure file";
     }
 
-    if (!android::base::ReadFdToString(fd, content)) {
-        *err = "Unable to read '" + path + "': " + strerror(errno);
-        return false;
+    std::string content;
+    if (!android::base::ReadFdToString(fd, &content)) {
+        return ErrnoError() << "Unable to read file contents";
     }
-    return true;
+    return content;
 }
 
-bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
-    *err = "";
-
+Result<Success> WriteFile(const std::string& path, const std::string& content) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
         open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
-        *err = "Unable to open '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "open() failed";
     }
     if (!android::base::WriteStringToFd(content, fd)) {
-        *err = "Unable to write to '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "Unable to write file contents";
     }
-    return true;
+    return Success();
 }
 
-int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
+bool mkdir_recursive(const std::string& path, mode_t mode) {
     std::string::size_type slash = 0;
     while ((slash = path.find('/', slash + 1)) != std::string::npos) {
         auto directory = path.substr(0, slash);
         struct stat info;
         if (stat(directory.c_str(), &info) != 0) {
-            auto ret = make_dir(directory.c_str(), mode, sehandle);
-            if (ret && errno != EEXIST) return ret;
+            auto ret = make_dir(directory, mode);
+            if (!ret && errno != EEXIST) return false;
         }
     }
-    auto ret = make_dir(path.c_str(), mode, sehandle);
-    if (ret && errno != EEXIST) return ret;
-    return 0;
+    auto ret = make_dir(path, mode);
+    if (!ret && errno != EEXIST) return false;
+    return true;
 }
 
 int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
-    boot_clock::time_point timeout_time = boot_clock::now() + timeout;
-    while (boot_clock::now() < timeout_time) {
+    android::base::Timer t;
+    while (t.duration() < timeout) {
         struct stat sb;
-        if (stat(filename, &sb) != -1) return 0;
-
+        if (stat(filename, &sb) != -1) {
+            LOG(INFO) << "wait for '" << filename << "' took " << t;
+            return 0;
+        }
         std::this_thread::sleep_for(10ms);
     }
+    LOG(WARNING) << "wait for '" << filename << "' timed out and took " << t;
     return -1;
 }
 
@@ -244,26 +232,21 @@
     }
 }
 
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
-    int rc;
-
-    char *secontext = NULL;
-
-    if (sehandle) {
-        selabel_lookup(sehandle, &secontext, path, mode);
-        setfscreatecon(secontext);
+bool make_dir(const std::string& path, mode_t mode) {
+    std::string secontext;
+    if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
-    rc = mkdir(path, mode);
+    int rc = mkdir(path.c_str(), mode);
 
-    if (secontext) {
+    if (!secontext.empty()) {
         int save_errno = errno;
-        freecon(secontext);
-        setfscreatecon(NULL);
+        setfscreatecon(nullptr);
         errno = save_errno;
     }
 
-    return rc;
+    return rc == 0;
 }
 
 /*
@@ -365,21 +348,31 @@
     return true;
 }
 
-void panic() {
-    LOG(ERROR) << "panic: rebooting to bootloader";
-    // Do not queue "shutdown" trigger since we want to shutdown immediately
-    DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
+static std::string init_android_dt_dir() {
+    // Use the standard procfs-based path by default
+    std::string android_dt_dir = kDefaultAndroidDtDir;
+    // The platform may specify a custom Android DT path in kernel cmdline
+    import_kernel_cmdline(false,
+                          [&](const std::string& key, const std::string& value, bool in_qemu) {
+                              if (key == "androidboot.android_dt_dir") {
+                                  android_dt_dir = value;
+                              }
+                          });
+    LOG(INFO) << "Using Android DT directory " << android_dt_dir;
+    return android_dt_dir;
 }
 
-std::ostream& operator<<(std::ostream& os, const Timer& t) {
-    os << t.duration_s() << " seconds";
-    return os;
+// FIXME: The same logic is duplicated in system/core/fs_mgr/
+const std::string& get_android_dt_dir() {
+    // Set once and saves time for subsequent calls to this function
+    static const std::string kAndroidDtDir = init_android_dt_dir();
+    return kAndroidDtDir;
 }
 
-// Reads the content of device tree file under kAndroidDtDir directory.
+// Reads the content of device tree file under the platform's Android DT directory.
 // Returns true if the read is success, false otherwise.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
-    const std::string file_name = kAndroidDtDir + sub_path;
+    const std::string file_name = get_android_dt_dir() + sub_path;
     if (android::base::ReadFileToString(file_name, dt_content)) {
         if (!dt_content->empty()) {
             dt_content->pop_back();  // Trims the trailing '\0' out.
@@ -398,3 +391,6 @@
     }
     return false;
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/util.h b/init/util.h
index 1ad6b77..2cfcf6c 100644
--- a/init/util.h
+++ b/init/util.h
@@ -28,54 +28,41 @@
 #include <android-base/chrono_utils.h>
 #include <selinux/label.h>
 
-#define COLDBOOT_DONE "/dev/.coldboot_done"
+#include "result.h"
 
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
+#define COLDBOOT_DONE "/dev/.coldboot_done"
 
 using android::base::boot_clock;
 using namespace std::chrono_literals;
 
+namespace android {
+namespace init {
+
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon, selabel_handle* sehandle);
+                 const char* socketcon);
 
-bool ReadFile(const std::string& path, std::string* content, std::string* err);
-bool WriteFile(const std::string& path, const std::string& content, std::string* err);
+Result<std::string> ReadFile(const std::string& path);
+Result<Success> WriteFile(const std::string& path, const std::string& content);
 
-class Timer {
-  public:
-    Timer() : start_(boot_clock::now()) {}
+Result<uid_t> DecodeUid(const std::string& name);
 
-    double duration_s() const {
-        typedef std::chrono::duration<double> double_duration;
-        return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
-    }
-
-    int64_t duration_ms() const {
-        return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
-            .count();
-    }
-
-  private:
-    android::base::boot_clock::time_point start_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Timer& t);
-
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
-
-int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
+bool mkdir_recursive(const std::string& pathname, mode_t mode);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
+bool make_dir(const std::string& path, mode_t mode);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
 bool expand_props(const std::string& src, std::string* dst);
 
-void panic() __attribute__((__noreturn__));
-
-// Reads or compares the content of device tree file under kAndroidDtDir directory.
+// Returns the platform's Android DT directory as specified in the kernel cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& get_android_dt_dir();
+// Reads or compares the content of device tree file under the platform's Android DT directory.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 4bb8a83..3ae53a4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -26,62 +26,55 @@
 
 using namespace std::literals::string_literals;
 
+namespace android {
+namespace init {
+
 TEST(util, ReadFile_ENOENT) {
-    std::string s("hello");
-    std::string err;
     errno = 0;
-    EXPECT_FALSE(ReadFile("/proc/does-not-exist", &s, &err));
-    EXPECT_EQ("Unable to open '/proc/does-not-exist': No such file or directory", err);
+    auto file_contents = ReadFile("/proc/does-not-exist");
     EXPECT_EQ(ENOENT, errno);
-    EXPECT_EQ("", s);  // s was cleared.
+    ASSERT_FALSE(file_contents);
+    EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
 }
 
 TEST(util, ReadFileGroupWriteable) {
     std::string s("hello");
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
-    EXPECT_EQ("", s);  // s was cleared.
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_FALSE(file_contents) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
 }
 
 TEST(util, ReadFileWorldWiteable) {
     std::string s("hello");
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
-    EXPECT_EQ("", s);  // s was cleared.
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_FALSE(file_contents) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
 }
 
 TEST(util, ReadFileSymbolicLink) {
-    std::string s("hello");
     errno = 0;
     // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
-    std::string err;
-    EXPECT_FALSE(ReadFile("/charger", &s, &err));
-    EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
+    auto file_contents = ReadFile("/charger");
     EXPECT_EQ(ELOOP, errno);
-    EXPECT_EQ("", s);  // s was cleared.
+    ASSERT_FALSE(file_contents);
+    EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
 }
 
 TEST(util, ReadFileSuccess) {
-    std::string s("hello");
-    std::string err;
-    EXPECT_TRUE(ReadFile("/proc/version", &s, &err));
-    EXPECT_EQ("", err);
-    EXPECT_GT(s.length(), 6U);
-    EXPECT_EQ('\n', s[s.length() - 1]);
-    s[5] = 0;
-    EXPECT_STREQ("Linux", s.c_str());
+    auto file_contents = ReadFile("/proc/version");
+    ASSERT_TRUE(file_contents);
+    EXPECT_GT(file_contents->length(), 6U);
+    EXPECT_EQ('\n', file_contents->at(file_contents->length() - 1));
+    (*file_contents)[5] = 0;
+    EXPECT_STREQ("Linux", file_contents->c_str());
 }
 
 TEST(util, WriteFileBinary) {
@@ -92,29 +85,23 @@
     ASSERT_EQ(10u, contents.size());
 
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, contents)) << strerror(errno);
 
-    std::string read_back_contents;
-    EXPECT_TRUE(ReadFile(tf.path, &read_back_contents, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
-    EXPECT_EQ(contents, read_back_contents);
-    EXPECT_EQ(10u, read_back_contents.size());
+    auto read_back_contents = ReadFile(tf.path);
+    ASSERT_TRUE(read_back_contents) << strerror(errno);
+    EXPECT_EQ(contents, *read_back_contents);
+    EXPECT_EQ(10u, read_back_contents->size());
 }
 
 TEST(util, WriteFileNotExist) {
     std::string s("hello");
-    std::string s2("hello");
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
-    std::string err;
-    EXPECT_TRUE(WriteFile(path, s, &err));
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(s, s2);
+    EXPECT_TRUE(WriteFile(path, s));
+    auto file_contents = ReadFile(path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ(s, *file_contents);
     struct stat sb;
     int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
     EXPECT_NE(-1, fd);
@@ -124,37 +111,30 @@
 }
 
 TEST(util, WriteFileExist) {
-    std::string s2("");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    std::string err;
-    EXPECT_TRUE(WriteFile(tf.path, "1hello1", &err)) << strerror(errno);
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_STREQ("1hello1", s2.c_str());
-    EXPECT_TRUE(WriteFile(tf.path, "2ll2", &err));
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_STREQ("2ll2", s2.c_str());
+    EXPECT_TRUE(WriteFile(tf.path, "1hello1")) << strerror(errno);
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ("1hello1", *file_contents);
+    EXPECT_TRUE(WriteFile(tf.path, "2ll2"));
+    file_contents = ReadFile(tf.path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ("2ll2", *file_contents);
 }
 
 TEST(util, DecodeUid) {
-    uid_t decoded_uid;
-    std::string err;
+    auto decoded_uid = DecodeUid("root");
+    EXPECT_TRUE(decoded_uid);
+    EXPECT_EQ(0U, *decoded_uid);
 
-    EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(0U, decoded_uid);
+    decoded_uid = DecodeUid("toot");
+    EXPECT_FALSE(decoded_uid);
+    EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
 
-    EXPECT_FALSE(DecodeUid("toot", &decoded_uid, &err));
-    EXPECT_EQ("getpwnam failed: No such file or directory", err);
-    EXPECT_EQ(UINT_MAX, decoded_uid);
-
-    EXPECT_TRUE(DecodeUid("123", &decoded_uid, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(123U, decoded_uid);
+    decoded_uid = DecodeUid("123");
+    EXPECT_TRUE(decoded_uid);
+    EXPECT_EQ(123U, *decoded_uid);
 }
 
 TEST(util, is_dir) {
@@ -167,7 +147,7 @@
 TEST(util, mkdir_recursive) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    EXPECT_TRUE(mkdir_recursive(path, 0755));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
@@ -179,7 +159,7 @@
 TEST(util, mkdir_recursive_extra_slashes) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    EXPECT_TRUE(mkdir_recursive(path, 0755));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
@@ -187,3 +167,6 @@
     std::string path3 = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index 7baa487..e0164b4 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -31,6 +31,9 @@
 
 #define DEV_NAME "/dev/watchdog"
 
+namespace android {
+namespace init {
+
 int watchdogd_main(int argc, char **argv) {
     InitKernelLogging(argv);
 
@@ -73,3 +76,6 @@
         sleep(interval);
     }
 }
+
+}  // namespace init
+}  // namespace android
diff --git a/init/watchdogd.h b/init/watchdogd.h
index 8b48ab8..73f77d5 100644
--- a/init/watchdogd.h
+++ b/init/watchdogd.h
@@ -17,6 +17,12 @@
 #ifndef _INIT_WATCHDOGD_H_
 #define _INIT_WATCHDOGD_H_
 
+namespace android {
+namespace init {
+
 int watchdogd_main(int argc, char **argv);
 
+}  // namespace init
+}  // namespace android
+
 #endif
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index b0ac5c4..29ffe32 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -8,7 +8,6 @@
         "-Wall",
         "-Werror",
     ],
-    clang: true
 }
 
 cc_library_shared {
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
new file mode 100644
index 0000000..9a637ac
--- /dev/null
+++ b/libasyncio/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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.
+//
+
+libasyncio_cppflags = [
+    "-Wall",
+    "-Wextra",
+    "-Werror",
+]
+
+cc_library {
+    name: "libasyncio",
+    vendor_available: true,
+    host_supported: true,
+    srcs: [
+        "AsyncIO.cpp",
+    ],
+    cppflags: libasyncio_cppflags,
+
+    export_include_dirs: ["include"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libasyncio/AsyncIO.cpp b/libasyncio/AsyncIO.cpp
new file mode 100644
index 0000000..7430bc8
--- /dev/null
+++ b/libasyncio/AsyncIO.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <asyncio/AsyncIO.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+int io_setup(unsigned nr, aio_context_t* ctxp) {
+    memset(ctxp, 0, sizeof(*ctxp));
+    return syscall(__NR_io_setup, nr, ctxp);
+}
+
+int io_destroy(aio_context_t ctx) {
+    return syscall(__NR_io_destroy, ctx);
+}
+
+int io_submit(aio_context_t ctx, long nr, iocb** iocbpp) {
+    return syscall(__NR_io_submit, ctx, nr, iocbpp);
+}
+
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout) {
+    return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);
+}
+
+int io_cancel(aio_context_t ctx, iocb* iocbp, io_event* result) {
+    return syscall(__NR_io_cancel, ctx, iocbp, result);
+}
+
+void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read) {
+    memset(iocb, 0, sizeof(*iocb));
+    iocb->aio_fildes = fd;
+    iocb->aio_lio_opcode = read ? IOCB_CMD_PREAD : IOCB_CMD_PWRITE;
+    iocb->aio_reqprio = 0;
+    iocb->aio_buf = reinterpret_cast<uint64_t>(buf);
+    iocb->aio_nbytes = count;
+    iocb->aio_offset = offset;
+}
diff --git a/libasyncio/include/asyncio/AsyncIO.h b/libasyncio/include/asyncio/AsyncIO.h
new file mode 100644
index 0000000..e3fb93a
--- /dev/null
+++ b/libasyncio/include/asyncio/AsyncIO.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ASYNCIO_H
+#define _ASYNCIO_H
+
+#include <cstring>
+#include <cstdint>
+#include <linux/aio_abi.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Provides kernel aio operations.
+ */
+
+int io_setup(unsigned nr, aio_context_t* ctxp);
+int io_destroy(aio_context_t ctx);
+int io_submit(aio_context_t ctx, long nr, iocb** iocbpp);
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout);
+int io_cancel(aio_context_t ctx, iocb*, io_event* result);
+void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif  // ASYNCIO_H
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index a643a29..b2c0c0f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -53,6 +53,8 @@
     "UnwindCurrent.cpp",
     "UnwindMap.cpp",
     "UnwindPtrace.cpp",
+    "UnwindStack.cpp",
+    "UnwindStackMap.cpp",
 ]
 
 cc_library_headers {
@@ -63,7 +65,11 @@
 
 cc_library {
     name: "libbacktrace",
-    vendor_available: true,
+    vendor_available: false,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     defaults: ["libbacktrace_common"],
     host_supported: true,
 
@@ -84,33 +90,13 @@
                 "libbase",
                 "liblog",
                 "libunwind",
-            ],
-
-            static_libs: ["libcutils"],
-            host_ldlibs: ["-lrt"],
-        },
-        linux_bionic: {
-            enabled: true,
-            srcs: libbacktrace_sources,
-
-            shared_libs: [
-                "libbase",
-                "liblog",
-                "libunwind",
+                "libunwindstack",
             ],
 
             static_libs: ["libcutils"],
         },
         android: {
-            srcs: libbacktrace_sources,
-
-            shared_libs: [
-                "libbase",
-                "liblog",
-                "libunwind",
-            ],
-
-            static_libs: ["libasync_safe", "libcutils"],
+            static_libs: ["libasync_safe"],
         },
     },
     whole_static_libs: ["libdemangle"],
@@ -130,11 +116,7 @@
         linux: {
             shared_libs: [
                 "libunwind",
-            ],
-        },
-        android: {
-            shared_libs: [
-                "libunwind",
+                "libunwindstack",
             ],
         },
     }
@@ -161,6 +143,8 @@
     shared_libs = [
         "libbase",
         "libunwind",
+        "libunwindstack",
+        "libziparchive",
     ],
 }
 
@@ -191,6 +175,7 @@
         "libcutils",
         "liblog",
         "libunwind",
+        "libunwindstack",
     ],
 
     group_static_libs: true,
@@ -215,18 +200,35 @@
         android: {
             cflags: ["-DENABLE_PSS_TESTS"],
             shared_libs: [
-                "libdl",
                 "libutils",
             ],
         },
-        linux: {
+        linux_glibc: {
             host_ldlibs: [
-                "-lpthread",
-                "-lrt",
-                "-ldl",
                 "-lncurses",
             ],
             static_libs: ["libutils"],
         },
     },
+
+    data: [
+        "testdata/arm/*",
+        "testdata/arm64/*",
+        "testdata/x86/*",
+        "testdata/x86_64/*",
+    ],
+}
+
+cc_benchmark {
+    name: "backtrace_benchmarks",
+    defaults: ["libbacktrace_common"],
+
+    srcs: [
+        "backtrace_benchmarks.cpp",
+    ],
+
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+    ],
 }
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index e46d353..81f5e32 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -84,10 +84,8 @@
 }
 
 std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
-  uintptr_t relative_pc;
   std::string map_name;
   if (BacktraceMap::IsValid(frame->map)) {
-    relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
     if (!frame->map.name.empty()) {
       map_name = frame->map.name.c_str();
       if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
@@ -99,10 +97,9 @@
     }
   } else {
     map_name = "<unknown>";
-    relative_pc = frame->pc;
   }
 
-  std::string line(StringPrintf("#%02zu pc %" PRIPTR "  ", frame->num, relative_pc));
+  std::string line(StringPrintf("#%02zu pc %" PRIPTR "  ", frame->num, frame->rel_pc));
   line += map_name;
   // Special handling for non-zero offset maps, we need to print that
   // information.
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 3c509e6..2c87fa8 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -133,6 +133,11 @@
           backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
           prev->stack_size = frame->sp - prev->sp;
         }
+        if (BacktraceMap::IsValid(frame->map)) {
+          frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias;
+        } else {
+          frame->rel_pc = frame->pc;
+        }
         num_frames++;
       } else {
         num_ignore_frames--;
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index af79562..0b8232b 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -57,7 +57,7 @@
     map.start = unw_map.start;
     map.end = unw_map.end;
     map.offset = unw_map.offset;
-    map.load_base = unw_map.load_base;
+    map.load_bias = unw_map.load_base;
     map.flags = unw_map.flags;
     map.name = unw_map.path;
 
@@ -106,7 +106,7 @@
       map.start = unw_map.start;
       map.end = unw_map.end;
       map.offset = unw_map.offset;
-      map.load_base = unw_map.load_base;
+      map.load_bias = unw_map.load_base;
       map.flags = unw_map.flags;
       map.name = unw_map.path;
 
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 42ac1bc..87282ef 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -135,6 +135,11 @@
       }
 
       FillInMap(frame->pc, &frame->map);
+      if (BacktraceMap::IsValid(frame->map)) {
+        frame->rel_pc = frame->pc - frame->map.start + frame->map.load_bias;
+      } else {
+        frame->rel_pc = frame->pc;
+      }
 
       frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
 
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
new file mode 100644
index 0000000..41153ce
--- /dev/null
+++ b/libbacktrace/UnwindStack.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#if !defined(__ANDROID__)
+#include <cutils/threads.h>
+#endif
+
+#include <backtrace/Backtrace.h>
+#include <demangle.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+#include <unwindstack/Unwinder.h>
+
+#include "BacktraceLog.h"
+#include "UnwindStack.h"
+#include "UnwindStackMap.h"
+
+static std::string GetFunctionName(BacktraceMap* back_map, uintptr_t pc, uintptr_t* offset) {
+  *offset = 0;
+  unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
+
+  // Get the map for this
+  unwindstack::MapInfo* map_info = maps->Find(pc);
+  if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
+    return "";
+  }
+
+  UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
+  unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
+
+  std::string name;
+  uint64_t func_offset;
+  if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+    return "";
+  }
+  *offset = func_offset;
+  return name;
+}
+
+static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+                   std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
+  static std::set<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
+  UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
+  auto process_memory = stack_map->process_memory();
+  unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
+                                 regs, stack_map->process_memory());
+  unwinder.Unwind(&skip_names);
+
+  if (num_ignore_frames >= unwinder.NumFrames()) {
+    frames->resize(0);
+    return true;
+  }
+
+  frames->resize(unwinder.NumFrames() - num_ignore_frames);
+  auto unwinder_frames = unwinder.frames();
+  size_t cur_frame = 0;
+  for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, cur_frame++) {
+    auto frame = &unwinder_frames[i];
+    backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
+
+    back_frame->num = frame->num;
+
+    back_frame->rel_pc = frame->rel_pc;
+    back_frame->pc = frame->pc;
+    back_frame->sp = frame->sp;
+
+    back_frame->func_name = frame->function_name;
+    back_frame->func_offset = frame->function_offset;
+
+    back_frame->map.name = frame->map_name;
+    back_frame->map.start = frame->map_start;
+    back_frame->map.end = frame->map_end;
+    back_frame->map.offset = frame->map_offset;
+    back_frame->map.load_bias = frame->map_load_bias;
+    back_frame->map.flags = frame->map_flags;
+  }
+
+  return true;
+}
+
+UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
+    : BacktraceCurrent(pid, tid, map) {}
+
+std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+  return ::GetFunctionName(GetMap(), pc, offset);
+}
+
+bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
+  std::unique_ptr<unwindstack::Regs> regs;
+  if (ucontext == nullptr) {
+    regs.reset(unwindstack::Regs::CreateFromLocal());
+    // Fill in the registers from this function. Do it here to avoid
+    // one extra function call appearing in the unwind.
+    unwindstack::RegsGetLocal(regs.get());
+  } else {
+    regs.reset(
+        unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentMachineType(), ucontext));
+  }
+
+  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
+}
+
+UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+    : BacktracePtrace(pid, tid, map) {}
+
+std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+  return ::GetFunctionName(GetMap(), pc, offset);
+}
+
+bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
+  std::unique_ptr<unwindstack::Regs> regs;
+  if (context == nullptr) {
+    regs.reset(unwindstack::Regs::RemoteGet(Tid()));
+  } else {
+    regs.reset(
+        unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentMachineType(), context));
+  }
+
+  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
+}
+
+Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {
+  if (pid == BACKTRACE_CURRENT_PROCESS) {
+    pid = getpid();
+    if (tid == BACKTRACE_CURRENT_THREAD) {
+      tid = gettid();
+    }
+  } else if (tid == BACKTRACE_CURRENT_THREAD) {
+    tid = pid;
+  }
+
+  if (map == nullptr) {
+// This would cause the wrong type of map object to be created, so disallow.
+#if defined(__ANDROID__)
+    __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__,
+              "Backtrace::CreateNew() must be called with a real map pointer.");
+#else
+    BACK_LOGE("Backtrace::CreateNew() must be called with a real map pointer.");
+    abort();
+#endif
+  }
+
+  if (pid == getpid()) {
+    return new UnwindStackCurrent(pid, tid, map);
+  } else {
+    return new UnwindStackPtrace(pid, tid, map);
+  }
+}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
new file mode 100644
index 0000000..be9ef63
--- /dev/null
+++ b/libbacktrace/UnwindStack.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 _LIBBACKTRACE_UNWIND_STACK_H
+#define _LIBBACKTRACE_UNWIND_STACK_H
+
+#include <stdint.h>
+
+#include <string>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktracePtrace.h"
+
+class UnwindStackCurrent : public BacktraceCurrent {
+ public:
+  UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
+  virtual ~UnwindStackCurrent() = default;
+
+  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
+
+  bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
+};
+
+class UnwindStackPtrace : public BacktracePtrace {
+ public:
+  UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+  virtual ~UnwindStackPtrace() = default;
+
+  bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
+
+  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+};
+
+#endif  // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
new file mode 100644
index 0000000..d4a2444
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/types.h>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "UnwindStackMap.h"
+
+//-------------------------------------------------------------------------
+UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
+
+bool UnwindStackMap::Build() {
+  if (pid_ == 0) {
+    pid_ = getpid();
+    stack_maps_.reset(new unwindstack::LocalMaps);
+  } else {
+    stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
+  }
+
+  // Create the process memory object.
+  process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
+
+  if (!stack_maps_->Parse()) {
+    return false;
+  }
+
+  // Iterate through the maps and fill in the backtrace_map_t structure.
+  for (auto& map_info : *stack_maps_) {
+    backtrace_map_t map;
+    map.start = map_info.start;
+    map.end = map_info.end;
+    map.offset = map_info.offset;
+    // Set to -1 so that it is demand loaded.
+    map.load_bias = static_cast<uintptr_t>(-1);
+    map.flags = map_info.flags;
+    map.name = map_info.name;
+
+    maps_.push_back(map);
+  }
+
+  return true;
+}
+
+void UnwindStackMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
+  BacktraceMap::FillIn(addr, map);
+  if (map->load_bias != static_cast<uintptr_t>(-1)) {
+    return;
+  }
+
+  // Fill in the load_bias.
+  unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
+  if (map_info == nullptr) {
+    return;
+  }
+  unwindstack::Elf* elf = map_info->GetElf(process_memory_, true);
+  map->load_bias = elf->GetLoadBias();
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::CreateNew(pid_t pid, bool uncached) {
+  BacktraceMap* map;
+
+  if (uncached) {
+    // Force use of the base class to parse the maps when this call is made.
+    map = new BacktraceMap(pid);
+  } else if (pid == getpid()) {
+    map = new UnwindStackMap(0);
+  } else {
+    map = new UnwindStackMap(pid);
+  }
+  if (!map->Build()) {
+    delete map;
+    return nullptr;
+  }
+  return map;
+}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
new file mode 100644
index 0000000..b93b340
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.h
@@ -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.
+ */
+
+#ifndef _LIBBACKTRACE_UNWINDSTACK_MAP_H
+#define _LIBBACKTRACE_UNWINDSTACK_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Maps.h>
+
+class UnwindStackMap : public BacktraceMap {
+ public:
+  explicit UnwindStackMap(pid_t pid);
+  ~UnwindStackMap() = default;
+
+  bool Build() override;
+
+  void FillIn(uintptr_t addr, backtrace_map_t* map) override;
+
+  unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
+
+  const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
+
+ protected:
+  std::unique_ptr<unwindstack::Maps> stack_maps_;
+  std::shared_ptr<unwindstack::Memory> process_memory_;
+};
+
+#endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
new file mode 100644
index 0000000..30c2a55
--- /dev/null
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+
+#include <benchmark/benchmark.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+// Definitions of prctl arguments to set a vma name in Android kernels.
+#define ANDROID_PR_SET_VMA 0x53564d41
+#define ANDROID_PR_SET_VMA_ANON_NAME 0
+
+constexpr size_t kNumMaps = 2000;
+constexpr size_t kNumIterations = 1000;
+
+static bool CountMaps(pid_t pid, size_t* num_maps) {
+  // Minimize the calls that might allocate memory. If too much memory
+  // gets allocated, then this routine will add extra maps and the next
+  // call will fail to get the same number of maps as before.
+  int fd =
+      open((std::string("/proc/") + std::to_string(pid) + "/maps").c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    fprintf(stderr, "Cannot open map file for pid %d: %s\n", pid, strerror(errno));
+    return false;
+  }
+  *num_maps = 0;
+  while (true) {
+    char buffer[2048];
+    ssize_t bytes = read(fd, buffer, sizeof(buffer));
+    if (bytes <= 0) {
+      break;
+    }
+    // Count the '\n'.
+    for (size_t i = 0; i < static_cast<size_t>(bytes); i++) {
+      if (buffer[i] == '\n') {
+        ++*num_maps;
+      }
+    }
+  }
+
+  close(fd);
+  return true;
+}
+
+static void CreateMap(benchmark::State& state, BacktraceMap* (*map_func)(pid_t, bool)) {
+  state.PauseTiming();
+  // Create a remote process so that the map data is exactly the same.
+  // Also, so that we can create a set number of maps.
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    size_t num_maps;
+    if (!CountMaps(getpid(), &num_maps)) {
+      exit(1);
+    }
+    // Create uniquely named maps.
+    std::vector<void*> maps;
+    for (size_t i = num_maps; i < kNumMaps; i++) {
+      int flags = PROT_READ | PROT_WRITE;
+      // Alternate page type to make sure a map entry is added for each call.
+      if ((i % 2) == 0) {
+        flags |= PROT_EXEC;
+      }
+      void* memory = mmap(nullptr, PAGE_SIZE, flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+      if (memory == MAP_FAILED) {
+        fprintf(stderr, "Failed to create map: %s\n", strerror(errno));
+        exit(1);
+      }
+      memset(memory, 0x1, PAGE_SIZE);
+      if (prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") ==
+          -1) {
+        fprintf(stderr, "Failed: %s\n", strerror(errno));
+      }
+      maps.push_back(memory);
+    }
+
+    if (!CountMaps(getpid(), &num_maps)) {
+      exit(1);
+    }
+
+    if (num_maps != kNumMaps) {
+      fprintf(stderr, "Maps set incorrectly: %zu found, %zu expected.\n", num_maps, kNumMaps);
+      std::string str;
+      android::base::ReadFileToString("/proc/self/maps", &str);
+      fprintf(stderr, "%s\n", str.c_str());
+      exit(1);
+    }
+
+    // Wait for an hour at most.
+    sleep(3600);
+    exit(1);
+  } else if (pid < 0) {
+    fprintf(stderr, "Fork failed: %s\n", strerror(errno));
+    return;
+  }
+
+  size_t num_maps = 0;
+  for (size_t i = 0; i < 2000; i++) {
+    if (CountMaps(pid, &num_maps) && num_maps == kNumMaps) {
+      break;
+    }
+    usleep(1000);
+  }
+  if (num_maps != kNumMaps) {
+    fprintf(stderr, "Timed out waiting for the number of maps available: %zu\n", num_maps);
+    return;
+  }
+
+  state.ResumeTiming();
+  while (state.KeepRunning()) {
+    for (size_t i = 0; i < static_cast<size_t>(state.range(0)); i++) {
+      BacktraceMap* map = map_func(pid, false);
+      if (map == nullptr) {
+        fprintf(stderr, "Failed to create map\n");
+        return;
+      }
+      delete map;
+    }
+  }
+  state.PauseTiming();
+
+  kill(pid, SIGKILL);
+  waitpid(pid, nullptr, 0);
+}
+
+static void BM_create_map(benchmark::State& state) {
+  CreateMap(state, BacktraceMap::Create);
+}
+BENCHMARK(BM_create_map)->Arg(kNumIterations);
+
+static void BM_create_map_new(benchmark::State& state) {
+  CreateMap(state, BacktraceMap::CreateNew);
+}
+BENCHMARK(BM_create_map_new)->Arg(kNumIterations);
+
+BENCHMARK_MAIN();
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 465b3f9..0a1f33d 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -27,6 +27,7 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <backtrace/Backtrace.h>
@@ -129,6 +130,10 @@
   return nullptr;
 }
 
+std::string GetTestPath(std::string path) {
+  return android::base::GetExecutableDirectory() + "/testdata/" + ABI_STRING + '/' + path;
+}
+
 // This test is disable because it is for generating test data.
 TEST(libbacktrace, DISABLED_generate_offline_testdata) {
   // Create a thread to generate the needed stack and registers information.
@@ -167,9 +172,9 @@
   // 2. Dump maps
   for (auto it = map->begin(); it != map->end(); ++it) {
     testdata += android::base::StringPrintf(
-        "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR
-        " load_base: %" PRIxPTR " flags: %d name: %s\n",
-        it->start, it->end, it->offset, it->load_base, it->flags, it->name.c_str());
+        "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR " load_bias: %" PRIxPTR
+        " flags: %d name: %s\n",
+        it->start, it->end, it->offset, it->load_bias, it->flags, it->name.c_str());
   }
   // 3. Dump registers
   testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context));
@@ -206,20 +211,6 @@
   return "";
 }
 
-static std::string GetArch() {
-#if defined(__arm__)
-  return "arm";
-#elif defined(__aarch64__)
-  return "aarch64";
-#elif defined(__i386__)
-  return "x86";
-#elif defined(__x86_64__)
-  return "x86_64";
-#else
-  return "";
-#endif
-}
-
 struct OfflineTestData {
   int pid;
   int tid;
@@ -246,9 +237,9 @@
       backtrace_map_t& map = testdata->maps.back();
       int pos;
       sscanf(line.c_str(),
-             "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR
-             " load_base: %" SCNxPTR " flags: %d name: %n",
-             &map.start, &map.end, &map.offset, &map.load_base, &map.flags, &pos);
+             "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR " load_bias: %" SCNxPTR
+             " flags: %d name: %n",
+             &map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos);
       map.name = android::base::Trim(line.substr(pos));
     } else if (android::base::StartsWith(line, "registers:")) {
       size_t size;
@@ -280,20 +271,15 @@
   return true;
 }
 
-static void BacktraceOfflineTest(const std::string& testlib_name) {
-  const std::string arch = GetArch();
-  if (arch.empty()) {
-    GTEST_LOG_(INFO) << "This test does nothing on current arch.";
-    return;
-  }
-  const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
-  struct stat st;
-  if (stat(testlib_path.c_str(), &st) == -1) {
-    GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
+static void BacktraceOfflineTest(const char* arch, const std::string& testlib_name) {
+  // TODO: For now, we can only run this on the same arch as the library arch.
+  if (std::string(ABI_STRING) != arch) {
+    GTEST_LOG_(INFO) << "Ignoring arch " << arch << " for lib " << testlib_name;
     return;
   }
 
-  const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
+  const std::string testlib_path(GetTestPath(testlib_name));
+  const std::string offline_testdata_path(GetTestPath("offline_testdata"));
   OfflineTestData testdata;
   ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
 
@@ -339,35 +325,40 @@
                                                       testdata.symbols));
 }
 
+// For now, these tests can only run on the given architectures.
 TEST(libbacktrace, offline_eh_frame) {
-  BacktraceOfflineTest("libbacktrace_test_eh_frame.so");
+  BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
+  BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
 }
 
 TEST(libbacktrace, offline_debug_frame) {
-  BacktraceOfflineTest("libbacktrace_test_debug_frame.so");
+  BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
+  BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
 }
 
 TEST(libbacktrace, offline_gnu_debugdata) {
-  BacktraceOfflineTest("libbacktrace_test_gnu_debugdata.so");
+  BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
+  BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
 }
 
 TEST(libbacktrace, offline_arm_exidx) {
-  BacktraceOfflineTest("libbacktrace_test_arm_exidx.so");
+  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) {
-  const std::string arch = GetArch();
-  if (arch.empty() || arch != "arm") {
-    GTEST_LOG_(INFO) << "This test does nothing on current arch.";
+  // TODO: For now, only run on the given arch.
+  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 = "testdata/" + arch + "/libart.so";
+  const std::string testlib_path(GetTestPath("libart.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 = "testdata/" + arch + "/offline_testdata_for_libart";
+  const std::string offline_testdata_path(GetTestPath("offline_testdata_for_libart"));
   OfflineTestData testdata;
   ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
 
@@ -392,8 +383,8 @@
   // 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_base;
+    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);
   }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index fb463b0..e5eb9e3 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -82,6 +82,14 @@
   int32_t done;
 };
 
+typedef Backtrace* (*create_func_t)(pid_t, pid_t, BacktraceMap*);
+typedef BacktraceMap* (*map_create_func_t)(pid_t, bool);
+
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+                            map_create_func_t map_func = nullptr);
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+                          map_create_func_t map_func = nullptr);
+
 static uint64_t NanoTime() {
   struct timespec t = { 0, 0 };
   clock_gettime(CLOCK_MONOTONIC, &t);
@@ -147,7 +155,7 @@
   return found;
 }
 
-static void VerifyLevelDump(Backtrace* backtrace) {
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
   ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
     << DumpFrames(backtrace);
   ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
@@ -189,7 +197,7 @@
   return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
 }
 
-static void VerifyMaxDump(Backtrace* backtrace) {
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
   ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
     << DumpFrames(backtrace);
   // Verify that the last frame is our recursive call.
@@ -251,10 +259,14 @@
 
 static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
                                const char* cur_proc) {
-  EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
-    << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
-  EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
-    << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2);
+  ASSERT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1) << "All backtrace:\n"
+                                                           << DumpFrames(bt_all)
+                                                           << "Ignore 1 backtrace:\n"
+                                                           << DumpFrames(bt_ign1);
+  ASSERT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2) << "All backtrace:\n"
+                                                           << DumpFrames(bt_all)
+                                                           << "Ignore 2 backtrace:\n"
+                                                           << DumpFrames(bt_ign2);
 
   // Check all of the frames are the same > the current frame.
   bool check = (cur_proc == nullptr);
@@ -304,8 +316,9 @@
   ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
 }
 
-static void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, bool (*ReadyFunc)(Backtrace*),
-                           void (*VerifyFunc)(Backtrace*)) {
+static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
+                           void (*VerifyFunc)(Backtrace*, create_func_t, map_create_func_t),
+                           create_func_t create_func, map_create_func_t map_create_func) {
   pid_t ptrace_tid;
   if (tid < 0) {
     ptrace_tid = pid;
@@ -322,15 +335,13 @@
       WaitForStop(ptrace_tid);
 
       std::unique_ptr<BacktraceMap> map;
-      if (share_map) {
-        map.reset(BacktraceMap::Create(pid));
-      }
-      std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+      map.reset(map_create_func(pid, false));
+      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());
       if (ReadyFunc(backtrace.get())) {
-        VerifyFunc(backtrace.get());
+        VerifyFunc(backtrace.get(), create_func, map_create_func);
         verified = true;
       } else {
         last_dump = DumpFrames(backtrace.get());
@@ -349,21 +360,22 @@
     ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
+                 Backtrace::Create, BacktraceMap::Create);
 
   kill(pid, SIGKILL);
   int status;
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-TEST(libbacktrace, ptrace_trace_shared_map) {
+TEST(libbacktrace, ptrace_trace_new) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
-
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
+                 Backtrace::CreateNew, BacktraceMap::CreateNew);
 
   kill(pid, SIGKILL);
   int status;
@@ -376,20 +388,37 @@
     ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
     _exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
+                 BacktraceMap::Create);
 
   kill(pid, SIGKILL);
   int status;
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-static void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
-  std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+TEST(libbacktrace, ptrace_max_trace_new) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
+    _exit(1);
+  }
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump,
+                 Backtrace::CreateNew, BacktraceMap::CreateNew);
+
+  kill(pid, SIGKILL);
+  int status;
+  ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+static void VerifyProcessIgnoreFrames(Backtrace* bt_all, create_func_t create_func,
+                                      map_create_func_t map_create_func) {
+  std::unique_ptr<BacktraceMap> map(map_create_func(bt_all->Pid(), false));
+  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());
 
-  std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+  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());
@@ -403,7 +432,22 @@
     ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
+                 Backtrace::Create, BacktraceMap::Create);
+
+  kill(pid, SIGKILL);
+  int status;
+  ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_ignore_frames_new) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    _exit(1);
+  }
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
+                 Backtrace::CreateNew, BacktraceMap::CreateNew);
 
   kill(pid, SIGKILL);
   int status;
@@ -466,7 +510,47 @@
     if (pid == *it) {
       continue;
     }
-    VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump);
+    VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump, Backtrace::Create,
+                   BacktraceMap::Create);
+  }
+
+  FinishRemoteProcess(pid);
+}
+
+TEST(libbacktrace, ptrace_threads_new) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
+      pthread_attr_t attr;
+      pthread_attr_init(&attr);
+      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+      pthread_t thread;
+      ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
+    }
+    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    _exit(1);
+  }
+
+  // Check to see that all of the threads are running before unwinding.
+  std::vector<pid_t> threads;
+  uint64_t start = NanoTime();
+  do {
+    usleep(US_PER_MSEC);
+    threads.clear();
+    GetThreads(pid, &threads);
+  } while ((threads.size() != NUM_PTRACE_THREADS + 1) && ((NanoTime() - start) <= 5 * NS_PER_SEC));
+  ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
+
+  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+  WaitForStop(pid);
+  for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
+    // Skip the current forked process, we only care about the threads.
+    if (pid == *it) {
+      continue;
+    }
+    VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump, Backtrace::CreateNew,
+                   BacktraceMap::CreateNew);
   }
 
   FinishRemoteProcess(pid);
@@ -784,6 +868,7 @@
   backtrace_frame_data_t frame;
   frame.num = 1;
   frame.pc = 2;
+  frame.rel_pc = 2;
   frame.sp = 0;
   frame.stack_size = 0;
   frame.func_offset = 0;
@@ -799,9 +884,10 @@
 
   // Check map name empty, but exists.
   frame.pc = 0xb0020;
+  frame.rel_pc = 0x20;
   frame.map.start = 0xb0000;
   frame.map.end = 0xbffff;
-  frame.map.load_base = 0;
+  frame.map.load_bias = 0;
 #if defined(__LP64__)
   EXPECT_EQ("#01 pc 0000000000000020  <anonymous:00000000000b0000>",
 #else
@@ -813,7 +899,7 @@
   frame.pc = 0xc0020;
   frame.map.start = 0xc0000;
   frame.map.end = 0xcffff;
-  frame.map.load_base = 0;
+  frame.map.load_bias = 0;
   frame.map.name = "[anon:thread signal stack]";
 #if defined(__LP64__)
   EXPECT_EQ("#01 pc 0000000000000020  [anon:thread signal stack:00000000000c0000]",
@@ -824,6 +910,7 @@
 
   // Check relative pc is set and map name is set.
   frame.pc = 0x12345679;
+  frame.rel_pc = 0x12345678;
   frame.map.name = "MapFake";
   frame.map.start =  1;
   frame.map.end =  1;
@@ -852,9 +939,10 @@
 #endif
             backtrace->FormatFrameData(&frame));
 
-  // Check func_name is set, func offset is non-zero, and load_base is non-zero.
+  // Check func_name is set, func offset is non-zero, and load_bias is non-zero.
+  frame.rel_pc = 0x123456dc;
   frame.func_offset = 645;
-  frame.map.load_base = 100;
+  frame.map.load_bias = 100;
 #if defined(__LP64__)
   EXPECT_EQ("#01 pc 00000000123456dc  MapFake (ProcFake+645)",
 #else
@@ -1575,7 +1663,7 @@
   munmap(device_map, DEVICE_MAP_SIZE);
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_remote) {
+TEST(libbacktrace, unwind_disallow_device_map_remote_new) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1584,13 +1672,11 @@
   CreateRemoteProcess(&pid);
 
   // Now create an unwind object.
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::CreateNew(pid));
+  ASSERT_TRUE(map.get() != nullptr);
+  std::unique_ptr<Backtrace> backtrace(Backtrace::CreateNew(pid, pid, map.get()));
 
-  // TODO: Currently unwind from context doesn't work on remote
-  // unwind. Keep this test because the new unwinder should support
-  // this eventually, or we can delete this test.
-  // properly with unwind from context.
-  // UnwindFromDevice(backtrace.get(), device_map);
+  UnwindFromDevice(backtrace.get(), device_map);
 
   FinishRemoteProcess(pid);
 
@@ -1629,7 +1715,8 @@
     ;
 }
 
-static void UnwindThroughSignal(bool use_action) {
+static void UnwindThroughSignal(bool use_action, create_func_t create_func,
+                                map_create_func_t map_create_func) {
   volatile int value = 0;
   pid_t pid;
   if ((pid = fork()) == 0) {
@@ -1655,7 +1742,8 @@
 
     WaitForStop(pid);
 
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+    std::unique_ptr<BacktraceMap> map(map_create_func(pid, false));
+    std::unique_ptr<Backtrace> backtrace(create_func(pid, pid, map.get()));
 
     size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
                                         reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
@@ -1673,6 +1761,7 @@
   // Wait for the process to get to the signal handler loop.
   Backtrace::const_iterator frame_iter;
   start = NanoTime();
+  std::unique_ptr<BacktraceMap> map;
   std::unique_ptr<Backtrace> backtrace;
   while (true) {
     usleep(1000);
@@ -1681,7 +1770,9 @@
 
     WaitForStop(pid);
 
-    backtrace.reset(Backtrace::Create(pid, pid));
+    map.reset(map_create_func(pid, false));
+    ASSERT_TRUE(map.get() != nullptr);
+    backtrace.reset(create_func(pid, pid, map.get()));
     ASSERT_TRUE(backtrace->Unwind(0));
     bool found = false;
     for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
@@ -1737,9 +1828,21 @@
   FinishRemoteProcess(pid);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); }
+TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+  UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
+}
 
-TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); }
+TEST(libbacktrace, unwind_remote_through_signal_using_handler_new) {
+  UnwindThroughSignal(false, Backtrace::CreateNew, BacktraceMap::CreateNew);
+}
+
+TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+  UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
+}
+
+TEST(libbacktrace, unwind_remote_through_signal_using_action_new) {
+  UnwindThroughSignal(true, Backtrace::CreateNew, BacktraceMap::CreateNew);
+}
 
 #if defined(ENABLE_PSS_TESTS)
 #include "GetPss.h"
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 4f73a65..289fd0c 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -55,6 +55,7 @@
 struct backtrace_frame_data_t {
   size_t num;             // The current fame number.
   uintptr_t pc;           // The absolute pc.
+  uintptr_t rel_pc;       // The relative pc.
   uintptr_t sp;           // The top of the stack.
   size_t stack_size;      // The size of the stack, zero indicate an unknown stack size.
   backtrace_map_t map;    // The map associated with the given pc.
@@ -89,6 +90,8 @@
   // If map is NULL, then create the map and manage it internally.
   // If map is not NULL, the map is still owned by the caller.
   static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+  // Same as above, but uses a different underlying unwinder.
+  static Backtrace* CreateNew(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
 
   // Create an offline Backtrace object that can be used to do an unwind without a process
   // that is still running. If cache_file is set to true, then elf information will be cached
@@ -124,7 +127,7 @@
   // Create a string representing the formatted line of backtrace information
   // for a single frame.
   virtual std::string FormatFrameData(size_t frame_num);
-  virtual std::string FormatFrameData(const backtrace_frame_data_t* frame);
+  static std::string FormatFrameData(const backtrace_frame_data_t* frame);
 
   pid_t Pid() const { return pid_; }
   pid_t Tid() const { return tid_; }
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index 8ab0dfa..6cf8b3f 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -41,7 +41,7 @@
   uintptr_t start = 0;
   uintptr_t end = 0;
   uintptr_t offset = 0;
-  uintptr_t load_base = 0;
+  uintptr_t load_bias = 0;
   int flags = 0;
   std::string name;
 };
@@ -52,6 +52,8 @@
   // Passing a map created with uncached set to true to Backtrace::Create()
   // is unsupported.
   static BacktraceMap* Create(pid_t pid, bool uncached = false);
+  // Same as above, but is compatible with the new unwinder.
+  static BacktraceMap* CreateNew(pid_t pid, bool uncached = false);
 
   static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
 
@@ -89,20 +91,14 @@
   const_iterator begin() const { return maps_.begin(); }
   const_iterator end() const { return maps_.end(); }
 
+  size_t size() const { return maps_.size(); }
+
   virtual bool Build();
 
   static inline bool IsValid(const backtrace_map_t& map) {
     return map.end > 0;
   }
 
-  static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) {
-    if (IsValid(map)) {
-      return pc - map.start + map.load_base;
-    } else {
-      return pc;
-    }
-  }
-
 protected:
   BacktraceMap(pid_t pid);
 
diff --git a/libbacktrace/include/backtrace/backtrace_constants.h b/libbacktrace/include/backtrace/backtrace_constants.h
index f8c1575..373a1e5 100644
--- a/libbacktrace/include/backtrace/backtrace_constants.h
+++ b/libbacktrace/include/backtrace/backtrace_constants.h
@@ -20,10 +20,10 @@
 // When the pid to be traced is set to this value, then trace the current
 // process. If the tid value is not BACKTRACE_NO_TID, then the specified
 // thread from the current process will be traced.
-#define BACKTRACE_CURRENT_PROCESS -1
+#define BACKTRACE_CURRENT_PROCESS (-1)
 // When the tid to be traced is set to this value, then trace the specified
 // current thread of the specified pid.
-#define BACKTRACE_CURRENT_THREAD -1
+#define BACKTRACE_CURRENT_THREAD (-1)
 
 #define MAX_BACKTRACE_FRAMES 64
 
diff --git a/libbacktrace/testdata/arm/offline_testdata b/libbacktrace/testdata/arm/offline_testdata
index 43d305a..6acea29 100644
--- a/libbacktrace/testdata/arm/offline_testdata
+++ b/libbacktrace/testdata/arm/offline_testdata
@@ -1,98 +1,98 @@
 pid: 32232 tid: 32233
-map: start: aad19000 end: aad6c000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test32
-map: start: aad6c000 end: aad6e000 offset: 52000 load_base: 0 flags: 1 name: /data/backtrace_test32
-map: start: aad6e000 end: aad6f000 offset: 54000 load_base: 0 flags: 3 name: /data/backtrace_test32
-map: start: e7380000 end: e7400000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e745f000 end: e7463000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libnetd_client.so
-map: start: e7463000 end: e7464000 offset: 3000 load_base: 0 flags: 1 name: /system/lib/libnetd_client.so
-map: start: e7464000 end: e7465000 offset: 4000 load_base: 0 flags: 3 name: /system/lib/libnetd_client.so
-map: start: e7480000 end: e7500000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e7558000 end: e756c000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libunwind.so
-map: start: e756c000 end: e756d000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e756d000 end: e756e000 offset: 14000 load_base: 0 flags: 1 name: /system/lib/libunwind.so
-map: start: e756e000 end: e756f000 offset: 15000 load_base: 0 flags: 3 name: /system/lib/libunwind.so
-map: start: e756f000 end: e75b5000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e75d4000 end: e75e1000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbase.so
-map: start: e75e1000 end: e75e2000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libbase.so
-map: start: e75e2000 end: e75e3000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libbase.so
-map: start: e7600000 end: e7616000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblzma.so
-map: start: e7616000 end: e7617000 offset: 15000 load_base: 0 flags: 1 name: /system/lib/liblzma.so
-map: start: e7617000 end: e7618000 offset: 16000 load_base: 0 flags: 3 name: /system/lib/liblzma.so
-map: start: e7618000 end: e761d000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7647000 end: e7656000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblog.so
-map: start: e7656000 end: e7657000 offset: e000 load_base: 0 flags: 1 name: /system/lib/liblog.so
-map: start: e7657000 end: e7658000 offset: f000 load_base: 0 flags: 3 name: /system/lib/liblog.so
-map: start: e7681000 end: e76a2000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libm.so
-map: start: e76a2000 end: e76a3000 offset: 20000 load_base: 0 flags: 1 name: /system/lib/libm.so
-map: start: e76a3000 end: e76a4000 offset: 21000 load_base: 0 flags: 3 name: /system/lib/libm.so
-map: start: e76eb000 end: e76ee000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
-map: start: e76ee000 end: e76ef000 offset: 2000 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
-map: start: e76ef000 end: e76f0000 offset: 3000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
-map: start: e7712000 end: e771f000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbacktrace.so
-map: start: e771f000 end: e7720000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e7720000 end: e7721000 offset: d000 load_base: 0 flags: 1 name: /system/lib/libbacktrace.so
-map: start: e7721000 end: e7722000 offset: e000 load_base: 0 flags: 3 name: /system/lib/libbacktrace.so
-map: start: e7761000 end: e7778000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libutils.so
-map: start: e7778000 end: e7779000 offset: 16000 load_base: 0 flags: 1 name: /system/lib/libutils.so
-map: start: e7779000 end: e777a000 offset: 17000 load_base: 0 flags: 3 name: /system/lib/libutils.so
-map: start: e77a5000 end: e782d000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libc.so
-map: start: e782d000 end: e7831000 offset: 87000 load_base: 0 flags: 1 name: /system/lib/libc.so
-map: start: e7831000 end: e7833000 offset: 8b000 load_base: 0 flags: 3 name: /system/lib/libc.so
-map: start: e7833000 end: e7834000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7834000 end: e7835000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
-map: start: e7835000 end: e783b000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7845000 end: e8437000 offset: 0 load_base: 2b000 flags: 5 name: /system/lib/libLLVM.so
-map: start: e8437000 end: e8438000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e8438000 end: e848a000 offset: bf2000 load_base: 0 flags: 1 name: /system/lib/libLLVM.so
-map: start: e848a000 end: e848b000 offset: c44000 load_base: 0 flags: 3 name: /system/lib/libLLVM.so
-map: start: e848b000 end: e84a1000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e84eb000 end: e84f7000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libcutils.so
-map: start: e84f7000 end: e84f8000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e84f8000 end: e84f9000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libcutils.so
-map: start: e84f9000 end: e84fa000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libcutils.so
-map: start: e852e000 end: e85b3000 offset: 0 load_base: 2000 flags: 5 name: /system/lib/libc++.so
-map: start: e85b3000 end: e85b4000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e85b4000 end: e85b8000 offset: 85000 load_base: 0 flags: 1 name: /system/lib/libc++.so
-map: start: e85b8000 end: e85b9000 offset: 89000 load_base: 0 flags: 3 name: /system/lib/libc++.so
-map: start: e85b9000 end: e85ba000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e85ce000 end: e85cf000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: e85e4000 end: e85e5000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e8607000 end: e8608000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e8680000 end: e8700000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e870d000 end: e8719000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: e8719000 end: e871b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: e871b000 end: e873b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
-map: start: e873b000 end: e875b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: e875b000 end: e875c000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e875c000 end: e875d000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: e875d000 end: e875e000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: e875e000 end: e875f000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e875f000 end: e877f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
-map: start: e877f000 end: e879f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: e879f000 end: e87a0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a0000 end: e87a1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87a1000 end: e87a2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a2000 end: e87a3000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e87a3000 end: e87a4000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: e87a4000 end: e87a5000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e87a5000 end: e87a6000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
-map: start: e87a6000 end: e87a7000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e87a7000 end: e87a8000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a8000 end: e87a9000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87a9000 end: e87aa000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87aa000 end: e87ab000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87ab000 end: e87ac000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: e87ac000 end: e87ad000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
-map: start: e87ad000 end: e87af000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
-map: start: e87af000 end: e87b0000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: e87b0000 end: e880d000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker
-map: start: e880d000 end: e880f000 offset: 5c000 load_base: 0 flags: 1 name: /system/bin/linker
-map: start: e880f000 end: e8810000 offset: 5e000 load_base: 0 flags: 3 name: /system/bin/linker
-map: start: e8810000 end: e8812000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: e8812000 end: e8813000 offset: 0 load_base: 0 flags: 1 name: 
-map: start: e8813000 end: e8815000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: ff886000 end: ff8a9000 offset: 0 load_base: 0 flags: 3 name: [stack]
-map: start: ffff0000 end: ffff1000 offset: 0 load_base: 0 flags: 5 name: [vectors]
+map: start: aad19000 end: aad6c000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test32
+map: start: aad6c000 end: aad6e000 offset: 52000 load_bias: 0 flags: 1 name: /data/backtrace_test32
+map: start: aad6e000 end: aad6f000 offset: 54000 load_bias: 0 flags: 3 name: /data/backtrace_test32
+map: start: e7380000 end: e7400000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e745f000 end: e7463000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libnetd_client.so
+map: start: e7463000 end: e7464000 offset: 3000 load_bias: 0 flags: 1 name: /system/lib/libnetd_client.so
+map: start: e7464000 end: e7465000 offset: 4000 load_bias: 0 flags: 3 name: /system/lib/libnetd_client.so
+map: start: e7480000 end: e7500000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e7558000 end: e756c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libunwind.so
+map: start: e756c000 end: e756d000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e756d000 end: e756e000 offset: 14000 load_bias: 0 flags: 1 name: /system/lib/libunwind.so
+map: start: e756e000 end: e756f000 offset: 15000 load_bias: 0 flags: 3 name: /system/lib/libunwind.so
+map: start: e756f000 end: e75b5000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e75d4000 end: e75e1000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbase.so
+map: start: e75e1000 end: e75e2000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libbase.so
+map: start: e75e2000 end: e75e3000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libbase.so
+map: start: e7600000 end: e7616000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblzma.so
+map: start: e7616000 end: e7617000 offset: 15000 load_bias: 0 flags: 1 name: /system/lib/liblzma.so
+map: start: e7617000 end: e7618000 offset: 16000 load_bias: 0 flags: 3 name: /system/lib/liblzma.so
+map: start: e7618000 end: e761d000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7647000 end: e7656000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblog.so
+map: start: e7656000 end: e7657000 offset: e000 load_bias: 0 flags: 1 name: /system/lib/liblog.so
+map: start: e7657000 end: e7658000 offset: f000 load_bias: 0 flags: 3 name: /system/lib/liblog.so
+map: start: e7681000 end: e76a2000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libm.so
+map: start: e76a2000 end: e76a3000 offset: 20000 load_bias: 0 flags: 1 name: /system/lib/libm.so
+map: start: e76a3000 end: e76a4000 offset: 21000 load_bias: 0 flags: 3 name: /system/lib/libm.so
+map: start: e76eb000 end: e76ee000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: e76ee000 end: e76ef000 offset: 2000 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: e76ef000 end: e76f0000 offset: 3000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: e7712000 end: e771f000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbacktrace.so
+map: start: e771f000 end: e7720000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e7720000 end: e7721000 offset: d000 load_bias: 0 flags: 1 name: /system/lib/libbacktrace.so
+map: start: e7721000 end: e7722000 offset: e000 load_bias: 0 flags: 3 name: /system/lib/libbacktrace.so
+map: start: e7761000 end: e7778000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libutils.so
+map: start: e7778000 end: e7779000 offset: 16000 load_bias: 0 flags: 1 name: /system/lib/libutils.so
+map: start: e7779000 end: e777a000 offset: 17000 load_bias: 0 flags: 3 name: /system/lib/libutils.so
+map: start: e77a5000 end: e782d000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libc.so
+map: start: e782d000 end: e7831000 offset: 87000 load_bias: 0 flags: 1 name: /system/lib/libc.so
+map: start: e7831000 end: e7833000 offset: 8b000 load_bias: 0 flags: 3 name: /system/lib/libc.so
+map: start: e7833000 end: e7834000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7834000 end: e7835000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: e7835000 end: e783b000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7845000 end: e8437000 offset: 0 load_bias: 2b000 flags: 5 name: /system/lib/libLLVM.so
+map: start: e8437000 end: e8438000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e8438000 end: e848a000 offset: bf2000 load_bias: 0 flags: 1 name: /system/lib/libLLVM.so
+map: start: e848a000 end: e848b000 offset: c44000 load_bias: 0 flags: 3 name: /system/lib/libLLVM.so
+map: start: e848b000 end: e84a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e84eb000 end: e84f7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libcutils.so
+map: start: e84f7000 end: e84f8000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e84f8000 end: e84f9000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libcutils.so
+map: start: e84f9000 end: e84fa000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libcutils.so
+map: start: e852e000 end: e85b3000 offset: 0 load_bias: 2000 flags: 5 name: /system/lib/libc++.so
+map: start: e85b3000 end: e85b4000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e85b4000 end: e85b8000 offset: 85000 load_bias: 0 flags: 1 name: /system/lib/libc++.so
+map: start: e85b8000 end: e85b9000 offset: 89000 load_bias: 0 flags: 3 name: /system/lib/libc++.so
+map: start: e85b9000 end: e85ba000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e85ce000 end: e85cf000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e85e4000 end: e85e5000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e8607000 end: e8608000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e8680000 end: e8700000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e870d000 end: e8719000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: e8719000 end: e871b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e871b000 end: e873b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: e873b000 end: e875b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e875b000 end: e875c000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e875c000 end: e875d000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e875d000 end: e875e000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e875e000 end: e875f000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e875f000 end: e877f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: e877f000 end: e879f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e879f000 end: e87a0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a0000 end: e87a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a1000 end: e87a2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a2000 end: e87a3000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e87a3000 end: e87a4000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: e87a4000 end: e87a5000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e87a5000 end: e87a6000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: e87a6000 end: e87a7000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e87a7000 end: e87a8000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a8000 end: e87a9000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a9000 end: e87aa000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87aa000 end: e87ab000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87ab000 end: e87ac000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e87ac000 end: e87ad000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: e87ad000 end: e87af000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: e87af000 end: e87b0000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e87b0000 end: e880d000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker
+map: start: e880d000 end: e880f000 offset: 5c000 load_bias: 0 flags: 1 name: /system/bin/linker
+map: start: e880f000 end: e8810000 offset: 5e000 load_bias: 0 flags: 3 name: /system/bin/linker
+map: start: e8810000 end: e8812000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: e8812000 end: e8813000 offset: 0 load_bias: 0 flags: 1 name: 
+map: start: e8813000 end: e8815000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: ff886000 end: ff8a9000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: ffff0000 end: ffff1000 offset: 0 load_bias: 0 flags: 5 name: [vectors]
 registers: 64 34868affdc8871e8150000001c0000001c000000150000000e00000007000000e08771e834868aff2354d2aa24f9ffffdc8871e88c8771e875b86ee778ba6ee7
 stack: start: e8715000 end: e8719000 size: 16384 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc8871e87dba6ee734868affdc8871e8dc8871e85dba6ee7070000000e000000150000001c000000dc8871e85dba6ee71c000000150000000e00000007000000100000000c0000000800000004000000ddb86ee75dba6ee7dc8871e804000000080000000c00000010000000dc8871e85dba6ee7100000000c000000080000000400000008000000060000000400000002000000288871e835b96ee75dba6ee7dc8871e802000000040000000600000008000000dc8871e85dba6ee70800000006000000040000000200000004000000030000000200000001000000708871e88db96ee75dba6ee7dc8871e801000000020000000300000004000000dc8871e85dba6ee70400000003000000020000000100000004000000208971e8208971e878000000e87d00003dba6ee75dba6ee7dc8871e878000000c5807ce7fc7183e734868aff78868aff78868aff34868aff34868aff78868affe0879437208971e84154d2aa0020000034868aff34868aff34868aff78000000c9b87ee7b1b87ee7a3f47be7288971e8b1b87ee7208971e800000000f83481e800000000e97d0000e87d000000000000005071e82039000000100000000000000000000000000000000000002354d2aa34868aff00000000002071e801000000000000000000000000000000708971e8208971e8000000000000000000000000e0879437000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 function: start: 0 end: e76eb835 name: unknown_start
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libart b/libbacktrace/testdata/arm/offline_testdata_for_libart
index 63f6a07..03e1df5 100644
--- a/libbacktrace/testdata/arm/offline_testdata_for_libart
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libart
@@ -1,10 +1,10 @@
 pid: 32232 tid: 32233
 registers: 64 000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e9
-map: start: e9380000 end: e9766000 offset: 0 load_base: b000 flags: 5 name: /system/lib/libart.so
+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
 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*)
 function: start: 2fbd35 end: 2fc789 name: art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)
 function: start: 2fcf75 end: 2fd88d name: art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)
-function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
\ No newline at end of file
+function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
diff --git a/libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so b/libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
similarity index 100%
rename from libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so
rename to libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/aarch64/offline_testdata b/libbacktrace/testdata/arm64/offline_testdata
similarity index 88%
rename from libbacktrace/testdata/aarch64/offline_testdata
rename to libbacktrace/testdata/arm64/offline_testdata
index ba44e09..75a2f12 100644
--- a/libbacktrace/testdata/aarch64/offline_testdata
+++ b/libbacktrace/testdata/arm64/offline_testdata
@@ -1,100 +1,100 @@
 pid: 32438 tid: 32439
-map: start: 557066e000 end: 55706ee000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test64
-map: start: 55706ef000 end: 55706f2000 offset: 80000 load_base: 0 flags: 1 name: /data/backtrace_test64
-map: start: 55706f2000 end: 55706f3000 offset: 83000 load_base: 0 flags: 3 name: /data/backtrace_test64
-map: start: 7014200000 end: 7014600000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: 701464c000 end: 701465c000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libcutils.so
-map: start: 701465c000 end: 701465d000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 701465d000 end: 701465e000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/libcutils.so
-map: start: 701465e000 end: 701465f000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/libcutils.so
-map: start: 7014691000 end: 70146b5000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblzma.so
-map: start: 70146b5000 end: 70146b6000 offset: 23000 load_base: 0 flags: 1 name: /system/lib64/liblzma.so
-map: start: 70146b6000 end: 70146b7000 offset: 24000 load_base: 0 flags: 3 name: /system/lib64/liblzma.so
-map: start: 70146b7000 end: 70146bc000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 70146c9000 end: 70158b5000 offset: 0 load_base: af000 flags: 5 name: /system/lib64/libLLVM.so
-map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_base: 0 flags: 1 name: /system/lib64/libLLVM.so
-map: start: 701596b000 end: 701596c000 offset: 12a1000 load_base: 0 flags: 3 name: /system/lib64/libLLVM.so
-map: start: 701596c000 end: 701599f000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 70159c2000 end: 70159f9000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libm.so
-map: start: 70159f9000 end: 70159fa000 offset: 36000 load_base: 0 flags: 1 name: /system/lib64/libm.so
-map: start: 70159fa000 end: 70159fb000 offset: 37000 load_base: 0 flags: 3 name: /system/lib64/libm.so
-map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbacktrace.so
-map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbacktrace.so
-map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbacktrace.so
-map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_base: 1000 flags: 5 name: /system/lib64/libutils.so
-map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_base: 0 flags: 1 name: /system/lib64/libutils.so
-map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_base: 0 flags: 3 name: /system/lib64/libutils.so
-map: start: 7015a99000 end: 7015b6d000 offset: 0 load_base: 9000 flags: 5 name: /system/lib64/libc++.so
-map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_base: 0 flags: 1 name: /system/lib64/libc++.so
-map: start: 7015b76000 end: 7015b77000 offset: dc000 load_base: 0 flags: 3 name: /system/lib64/libc++.so
-map: start: 7015b77000 end: 7015b7a000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015b81000 end: 7015b92000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblog.so
-map: start: 7015b92000 end: 7015b93000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/liblog.so
-map: start: 7015b93000 end: 7015b94000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/liblog.so
-map: start: 7015be3000 end: 7015ca3000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libc.so
-map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_base: 0 flags: 1 name: /system/lib64/libc.so
-map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_base: 0 flags: 3 name: /system/lib64/libc.so
-map: start: 7015cab000 end: 7015cac000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015cac000 end: 7015cad000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
-map: start: 7015cad000 end: 7015cb4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
-map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
-map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
-map: start: 7015d1f000 end: 7015d39000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libunwind.so
-map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_base: 0 flags: 1 name: /system/lib64/libunwind.so
-map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_base: 0 flags: 3 name: /system/lib64/libunwind.so
-map: start: 7015d3b000 end: 7015da4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015de8000 end: 7015df7000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbase.so
-map: start: 7015df7000 end: 7015df8000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7015df8000 end: 7015df9000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbase.so
-map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbase.so
-map: start: 7015e35000 end: 7015e36000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: 7015e4f000 end: 7015e50000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015f11000 end: 7015f13000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libnetd_client.so
-map: start: 7015f13000 end: 7015f14000 offset: 1000 load_base: 0 flags: 1 name: /system/lib64/libnetd_client.so
-map: start: 7015f14000 end: 7015f15000 offset: 2000 load_base: 0 flags: 3 name: /system/lib64/libnetd_client.so
-map: start: 7015f6c000 end: 7015f79000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7015f79000 end: 7015f99000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
-map: start: 7015f99000 end: 7015f9a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7015faf000 end: 7015fcf000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7015fdd000 end: 7015fde000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7015fde000 end: 7015ffe000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
-map: start: 7015ffe000 end: 701601e000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: 701601e000 end: 701601f000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 701601f000 end: 7016020000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7016020000 end: 7016021000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7016021000 end: 7016022000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7016022000 end: 7016023000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
-map: start: 7016023000 end: 7016025000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7016025000 end: 7016026000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7016026000 end: 7016027000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7016027000 end: 7016028000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7016028000 end: 7016029000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: 7016029000 end: 701602a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 701602a000 end: 701602b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: 701602b000 end: 701602c000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
-map: start: 701602c000 end: 7016030000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
-map: start: 7016030000 end: 7016031000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: 7016031000 end: 7016033000 offset: 0 load_base: 0 flags: 5 name: [vdso]
-map: start: 7016033000 end: 70160dd000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker64
-map: start: 70160dd000 end: 70160e0000 offset: a9000 load_base: 0 flags: 1 name: /system/bin/linker64
-map: start: 70160e0000 end: 70160e1000 offset: ac000 load_base: 0 flags: 3 name: /system/bin/linker64
-map: start: 70160e1000 end: 70160e4000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 70160e4000 end: 70160e5000 offset: 0 load_base: 0 flags: 1 name: 
-map: start: 70160e5000 end: 70160e8000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_base: 0 flags: 3 name: [stack]
+map: start: 557066e000 end: 55706ee000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test64
+map: start: 55706ef000 end: 55706f2000 offset: 80000 load_bias: 0 flags: 1 name: /data/backtrace_test64
+map: start: 55706f2000 end: 55706f3000 offset: 83000 load_bias: 0 flags: 3 name: /data/backtrace_test64
+map: start: 7014200000 end: 7014600000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: 701464c000 end: 701465c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libcutils.so
+map: start: 701465c000 end: 701465d000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 701465d000 end: 701465e000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/libcutils.so
+map: start: 701465e000 end: 701465f000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/libcutils.so
+map: start: 7014691000 end: 70146b5000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblzma.so
+map: start: 70146b5000 end: 70146b6000 offset: 23000 load_bias: 0 flags: 1 name: /system/lib64/liblzma.so
+map: start: 70146b6000 end: 70146b7000 offset: 24000 load_bias: 0 flags: 3 name: /system/lib64/liblzma.so
+map: start: 70146b7000 end: 70146bc000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70146c9000 end: 70158b5000 offset: 0 load_bias: af000 flags: 5 name: /system/lib64/libLLVM.so
+map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_bias: 0 flags: 1 name: /system/lib64/libLLVM.so
+map: start: 701596b000 end: 701596c000 offset: 12a1000 load_bias: 0 flags: 3 name: /system/lib64/libLLVM.so
+map: start: 701596c000 end: 701599f000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70159c2000 end: 70159f9000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libm.so
+map: start: 70159f9000 end: 70159fa000 offset: 36000 load_bias: 0 flags: 1 name: /system/lib64/libm.so
+map: start: 70159fa000 end: 70159fb000 offset: 37000 load_bias: 0 flags: 3 name: /system/lib64/libm.so
+map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbacktrace.so
+map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbacktrace.so
+map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbacktrace.so
+map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_bias: 1000 flags: 5 name: /system/lib64/libutils.so
+map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_bias: 0 flags: 1 name: /system/lib64/libutils.so
+map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_bias: 0 flags: 3 name: /system/lib64/libutils.so
+map: start: 7015a99000 end: 7015b6d000 offset: 0 load_bias: 9000 flags: 5 name: /system/lib64/libc++.so
+map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_bias: 0 flags: 1 name: /system/lib64/libc++.so
+map: start: 7015b76000 end: 7015b77000 offset: dc000 load_bias: 0 flags: 3 name: /system/lib64/libc++.so
+map: start: 7015b77000 end: 7015b7a000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015b81000 end: 7015b92000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblog.so
+map: start: 7015b92000 end: 7015b93000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/liblog.so
+map: start: 7015b93000 end: 7015b94000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/liblog.so
+map: start: 7015be3000 end: 7015ca3000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libc.so
+map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_bias: 0 flags: 1 name: /system/lib64/libc.so
+map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_bias: 0 flags: 3 name: /system/lib64/libc.so
+map: start: 7015cab000 end: 7015cac000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cac000 end: 7015cad000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: 7015cad000 end: 7015cb4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: 7015d1f000 end: 7015d39000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libunwind.so
+map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_bias: 0 flags: 1 name: /system/lib64/libunwind.so
+map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_bias: 0 flags: 3 name: /system/lib64/libunwind.so
+map: start: 7015d3b000 end: 7015da4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015de8000 end: 7015df7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbase.so
+map: start: 7015df7000 end: 7015df8000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7015df8000 end: 7015df9000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbase.so
+map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbase.so
+map: start: 7015e35000 end: 7015e36000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015e4f000 end: 7015e50000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015f11000 end: 7015f13000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libnetd_client.so
+map: start: 7015f13000 end: 7015f14000 offset: 1000 load_bias: 0 flags: 1 name: /system/lib64/libnetd_client.so
+map: start: 7015f14000 end: 7015f15000 offset: 2000 load_bias: 0 flags: 3 name: /system/lib64/libnetd_client.so
+map: start: 7015f6c000 end: 7015f79000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7015f79000 end: 7015f99000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: 7015f99000 end: 7015f9a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015faf000 end: 7015fcf000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7015fdd000 end: 7015fde000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015fde000 end: 7015ffe000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: 7015ffe000 end: 701601e000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 701601e000 end: 701601f000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 701601f000 end: 7016020000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7016020000 end: 7016021000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7016021000 end: 7016022000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7016022000 end: 7016023000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: 7016023000 end: 7016025000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7016025000 end: 7016026000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016026000 end: 7016027000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7016027000 end: 7016028000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016028000 end: 7016029000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016029000 end: 701602a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 701602a000 end: 701602b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 701602b000 end: 701602c000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: 701602c000 end: 7016030000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: 7016030000 end: 7016031000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016031000 end: 7016033000 offset: 0 load_bias: 0 flags: 5 name: [vdso]
+map: start: 7016033000 end: 70160dd000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker64
+map: start: 70160dd000 end: 70160e0000 offset: a9000 load_bias: 0 flags: 1 name: /system/bin/linker64
+map: start: 70160e0000 end: 70160e1000 offset: ac000 load_bias: 0 flags: 3 name: /system/bin/linker64
+map: start: 70160e1000 end: 70160e4000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 70160e4000 end: 70160e5000 offset: 0 load_bias: 0 flags: 1 name: 
+map: start: 70160e5000 end: 70160e8000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_bias: 0 flags: 3 name: [stack]
 registers: 4560 679a0b1670000000f3eb6d705500000090f56e7055000000000000000000000000206f705500000014e0677055000000d038bed87f0000004cde67705500000041000000000000001900000000000000c00f241470000000d3aec914588f4bcd0400000000000000e493af157000000090f56e7055000000060000000000000023c00b1670000000b1d1fd15700000003039bed87f000000c898041670000000304cbed87f000000b8130e1670000000303cbed87f000000f838bed87f0000000e0000000000000015000000000000001c00000000000000ec59cf1570000000b863fd15700000005064fd15700000000000000000000000ec59cf15700000000200000000000000b863fd1570000000144abed87f0000006064fd15700000005064fd157000000000010000000000005826bed87f000000d86fcf15700000006057cf157000000000000000000000005064fd15700000005064fd15700000005064fd1570000000b67e00000000000040fd677055000000d064fd15700000000030fd157000000002000000000000000100000000000000fcb58a56000000000063fd15700000009857cf1570000000c062fd15700000001c5acf157000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000003003167000000001000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000033000000000000000300000000000000003303167000000008330316700000000000000006000000f8320316700000005839bed87f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000c84cbed87f000000e84cbed87f000000984dbed87f00000078170e167000000002fd0000000000001400000000000000ff8100000100000000000000000000000000000000000000000000000000000078145700000000000010000000000000902b000000000000bdd04058000000000000000000000000bdd04058000000000000000000000000ccb58a560000000042487408000000000000000000000000cc57041670000000004704167000000010c0fd157000000010c0fd157000000090c0fd15700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000010000000000000002f48bed87f000000d3aec914588f4bcd2045bed87f0000002045bed87f0000002f48bed87f00000001000000000000002f000080000000005045bed87f0000000045bed87f000000c0a0c315700000006045bed87f0000006045bed87f000000010000000000000001000000000000000344bed87f00000001000000100000009c3fbed87f0000009b3fbed87f0000000344bed87f0000009a3fbed87f0000000100000000000000953fbed87f0000004344bed80100000001000000010000002c48bed87f0000000444bed87f0000004344bed80100000000000000000000000100000000000000b03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd000000000100000000000000000000000100000000000000f03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03fbed87f0000000000000000000000000000000000000000000000000000000000000000000000d3aec914588f4bcd08226f70550000000000000000000000000000000000000000000000000000001048bed87f0000008047bed87f0000006047bed87f000000e0ffffff80ffffff03000000000000000000000000000000891d6e7055000000d3aec914588f4bcd5047bed87f0000005047bed87f000000861d6e705500000003000000000000002f00008000000000f0a6ca15700000004047bed87f000000c0a0c31570000000891d6e7055000000d3aec914000000000100000000000000a047bed800000000f9006e70550000000100000000000000dc41bed87f000000db41bed87f0000004346bed87f000000da41bed87f0000000100000000000000d541bed87f00000001000000010000000100000001000000861d6e70550000004446bed87f0000002c42bed80300000000000000000000000100000000000000f041bed87f0000009b1e6e700100000000000000000000000000000000000000d3aec914588f4bcd8e1e6e70550000000d000000000000002f00008000000000f0a6ca15700000003048bed87f000000c0a0c31570000000661e6e700100000000000000000000002f000000000000001000000000000000e21e6e7055000000d3aec914588f4bcdb048bed87f000000b048bed87f000000e21e6e70550000002f000000000000002f00008000000000f0a6ca1570000000a048bed87f000000c0a0c315700000008e1e6e7055000000000000000000000022000000000000000000000000000000205827147000000022000000000000003c43bed87f0000003b43bed87f000000a347bed87f0000003a43bed87f00000001000000000000003543bed87f000000f048bed8010000000100000001000000dd1e6e7055000000a447bed87f0000008c43bed82f000000000000000000000001000000000000005043bed87f000000861d6e700300000000000000000000000000000000000000d3aec914588f4bcd721e6e7055000000f447bed87f000000981123141800000000000000000000000100000000000000a043bed87f000000861d6e70030000000000000000000000d042bed87f0000000000000000000000981123147000000098112314700000009811231470000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000504abed87f000000b049bed87f0000008049bed87f00000000000000000000004043bed87f0000000000000000000000991123147000000000000000000000008e1e6e70550000000d00000000000000a04abed87f000000000000000000000000000000000000000000000000000000504abed87f000000e049bed87f000000a049bed87f000000c8ffffff80ffffff591e6e70550000000d0000000000000098112314700000000000000000000000df1e6e7055000000010000000000000020582714700000002200000000000000f049bed87f000000c8ffffff80ffffff9811231470000000980023147000000098112314700000009811231470000000741e6e7055000000060000000000000009f12914700000000c00000000000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b149bed87f000000b149bed87f000000f049be317f000000f049bed87f000000f049bed87f000000b149bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000f149bed87f000000f049bed87f000000d3aec914588f4bcdfcb58a56000000006cbb687055000000160000000000000098112314700000009911231470000000991123147000000098112314700000009911231470000000fcb58a5600000000010000000000000000000000000000000100000000000000604a050000000000e845bed87f0000006048bed87f000000a046bed87f00000017000000000000002c48bed87f0000009046bed87f000000ac73c515700000009911231470000000d3aec914588f4bcd1048bed87f0000008047bed87f0000006047bed87f000000e8ffffff80ffffffffffffffffffffff99112314700000006148bed87f000000981123141500000008020000ffffffff6048bed87f000000160000000000000058112314700000001700000000000000a849bed87f0000000000000000000000284abed87f0000001700000000000000284abed87f000000284abed87f000000d249bed87f00000001000000000000009a112314700000001600000000000000284abed87f000000ffffffffffffffff284abed87f000000284abed87f000000284abed87f000000284abed87f0000001700000000000000ffffffffffffffff284abed87f000000284abed87f000000ff49bed87f000000284abed87f00000000000000000000000000000000000000d049bed87f000000d049bed87f000000004abed87f000000d049bed87f0000000100000000000000d049bed87f000000d049bed87f000000d049bed87f00000017000000000000000100000000000000ffffffffffffffff991123147000000001000000000000003448bed87f0000009911231470000000b0ca687055000000ffffffffffffffff010000000000000099112314700000007047bed87f000000d3aec914588f4bcdfcb58a56000000000100000000000000000000000000000050226f70550000000000000000000000b44b05000000000000102a1470000000861d6e70550000000300000000000000f0a6ca1570000000a047bed87f0000006c79c31570000000f048bed87f0000008048bed87f0000004048bed87f000000c8ffffff80ffffff0000000000000000d3aec914588f4bcdf849bed87f000000f0a6ca15700000000200000000000000085b0e1670000000e048bed87f0000002c6fc515700000009048bed87f000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a89912314700000000000000000000000e9216f70550000008991231470000000e449bed87f0000008991231470000000000000000000000000000000000000008891231470000000899123147000000089912314700000008891231470000000170000000000000088912314700000008891231470000000d3aec914588f4bcd899123147000000016000000000000000000000000000000e9216f705500000088912314700000008891231470000000889123147000000088912314700000008891231470000000f0a6ca15700000000049bed87f0000006c79c31570000000889123147000000088912314700000008891231470000000889123147000000088912314700000008891231470000000889123147000000089912314700000008991231470000000085b0e1670000000404abed87f0000002c6fc51570000000c80a6f7055000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a3b3325736d001b5b6d002c20776865726520002573203d202573000a526570650000000000000000000000000000000000000000000000008891231470000000000000000000000088912314700000008891231470000000889123147000000088912314700000000000000000000000889123147000000088912314700000008891231470000000470000000000000000502a1470000000d3aec914588f4bcdf05727147000000000b2221470000000604abed87f0000005c716c7055000000fcb58a56000000007c706c7055000000fcb58a5600000000ea4b050000000000
 stack: start: 7015fd3000 end: 7015fd7000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000004038bed87f000000b863fd1570000000b863fd1570000000b863fd1570000000ec59cf15700000001c000000150000000e000000070000003063fd15700000001c58cf1570000000b863fd1570000000ec59cf1570000000100000000c00000008000000040000006063fd15700000007c58cf1570000000b863fd1570000000ec59cf1570000000080000000600000004000000020000009063fd1570000000dc58cf1570000000b863fd1570000000ec59cf157000000004000000030000000200000001000000d063fd1570000000c459cf15700000000100000000000000144abed87f0000004038bed87f0000004038bed87f000000144abed87f000000d3aec914588f4bcd1064fd157000000074fd6770550000004038bed87f0000004038bed87f000000ec84c41570000000e484c41570000000c484c4157000000000000000000000004064fd15700000004015c01570000000b67e0000000000000000000000000000705a0e1670000000185b0e167000000000000000000000000000000000000000705a0e16700000000000000000000000b77e0000b67e000000000000550000000030fd157000000050340000000000000010000000000000000000000000000000b222147000000000102a14700000000000000000000000000000000000000040fd6770550000004038bed87f000000000000000000000000a0fa1570000000010000000000000000000000000000000000000000000000e864fd15700000005064fd1570000000000000000000000000000000000000000000000000000000d3aec914588f4bcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 function: start: 0 end: 7015cf5760 name: unknown_start
diff --git a/libbacktrace/testdata/x86/offline_testdata b/libbacktrace/testdata/x86/offline_testdata
index 3e4e06c..e8b7a99 100644
--- a/libbacktrace/testdata/x86/offline_testdata
+++ b/libbacktrace/testdata/x86/offline_testdata
@@ -1,75 +1,75 @@
 pid: 34545 tid: 34546
-map: start: f705a000 end: f705c000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f705c000 end: f707f000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f707f000 end: f7080000 offset: 22000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f7080000 end: f7081000 offset: 23000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f7081000 end: f7088000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7088000 end: f7230000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7230000 end: f7231000 offset: 1a8000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7231000 end: f7233000 offset: 1a8000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7233000 end: f7234000 offset: 1aa000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7234000 end: f7237000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7237000 end: f727b000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727b000 end: f727c000 offset: 43000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727c000 end: f727d000 offset: 44000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727d000 end: f7299000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
-map: start: f7299000 end: f729a000 offset: 1b000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
-map: start: f729a000 end: f72b8000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72b8000 end: f72b9000 offset: 1e000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72b9000 end: f72bb000 offset: 1e000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72bb000 end: f72bc000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72bc000 end: f72bd000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f72bd000 end: f72e0000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e0000 end: f72e1000 offset: 22000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e1000 end: f72e2000 offset: 23000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e2000 end: f72e5000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e5000 end: f72e6000 offset: 2000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e6000 end: f72e7000 offset: 3000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e7000 end: f72ee000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72ee000 end: f72ef000 offset: 6000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72ef000 end: f72f0000 offset: 7000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72f0000 end: f7308000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f7308000 end: f7309000 offset: 18000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f7309000 end: f730a000 offset: 19000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f730a000 end: f730c000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f732f000 end: f7331000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7331000 end: f7425000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f7425000 end: f7426000 offset: f4000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f7426000 end: f742a000 offset: f4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f742a000 end: f742b000 offset: f8000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f742b000 end: f742d000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f742d000 end: f7446000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7446000 end: f7447000 offset: 18000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7447000 end: f7448000 offset: 19000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7448000 end: f7457000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7457000 end: f745c000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745c000 end: f745d000 offset: 4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745d000 end: f745e000 offset: 5000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745e000 end: f7467000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7467000 end: f7468000 offset: 9000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7468000 end: f7469000 offset: 9000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7469000 end: f746a000 offset: a000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f746a000 end: f7477000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7477000 end: f7478000 offset: c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7478000 end: f7479000 offset: d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7479000 end: f7489000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f7489000 end: f748a000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f748a000 end: f748b000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f748b000 end: f748c000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f748c000 end: f748d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748d000 end: f748e000 offset: 0 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748e000 end: f748f000 offset: 1000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748f000 end: f7491000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7491000 end: f74b1000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b1000 end: f74b2000 offset: 1f000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b2000 end: f74b3000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b3000 end: f77c6000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77c6000 end: f77c7000 offset: 0 load_base: ffffe000 flags: 5 name: [vdso]
-map: start: f77c7000 end: f77d4000 offset: 313000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77d4000 end: f77d5000 offset: 320000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77d5000 end: f77d6000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7ec6000 end: f7ee7000 offset: 0 load_base: 0 flags: 3 name: [heap]
-map: start: ffe4e000 end: ffe70000 offset: 0 load_base: 0 flags: 3 name: [stack]
+map: start: f705a000 end: f705c000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f705c000 end: f707f000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f707f000 end: f7080000 offset: 22000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7080000 end: f7081000 offset: 23000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7081000 end: f7088000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7088000 end: f7230000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7230000 end: f7231000 offset: 1a8000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7231000 end: f7233000 offset: 1a8000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7233000 end: f7234000 offset: 1aa000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7234000 end: f7237000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7237000 end: f727b000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727b000 end: f727c000 offset: 43000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727c000 end: f727d000 offset: 44000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727d000 end: f7299000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f7299000 end: f729a000 offset: 1b000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f729a000 end: f72b8000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b8000 end: f72b9000 offset: 1e000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b9000 end: f72bb000 offset: 1e000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bb000 end: f72bc000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bc000 end: f72bd000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f72bd000 end: f72e0000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e0000 end: f72e1000 offset: 22000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e1000 end: f72e2000 offset: 23000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e2000 end: f72e5000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e5000 end: f72e6000 offset: 2000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e6000 end: f72e7000 offset: 3000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e7000 end: f72ee000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ee000 end: f72ef000 offset: 6000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ef000 end: f72f0000 offset: 7000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72f0000 end: f7308000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7308000 end: f7309000 offset: 18000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7309000 end: f730a000 offset: 19000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f730a000 end: f730c000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f732f000 end: f7331000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7331000 end: f7425000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7425000 end: f7426000 offset: f4000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7426000 end: f742a000 offset: f4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742a000 end: f742b000 offset: f8000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742b000 end: f742d000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f742d000 end: f7446000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7446000 end: f7447000 offset: 18000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7447000 end: f7448000 offset: 19000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7448000 end: f7457000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7457000 end: f745c000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745c000 end: f745d000 offset: 4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745d000 end: f745e000 offset: 5000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745e000 end: f7467000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7467000 end: f7468000 offset: 9000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7468000 end: f7469000 offset: 9000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7469000 end: f746a000 offset: a000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f746a000 end: f7477000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7477000 end: f7478000 offset: c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7478000 end: f7479000 offset: d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7479000 end: f7489000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f7489000 end: f748a000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748a000 end: f748b000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748b000 end: f748c000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f748c000 end: f748d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748d000 end: f748e000 offset: 0 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748e000 end: f748f000 offset: 1000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748f000 end: f7491000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7491000 end: f74b1000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b1000 end: f74b2000 offset: 1f000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b2000 end: f74b3000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b3000 end: f77c6000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77c6000 end: f77c7000 offset: 0 load_bias: ffffe000 flags: 5 name: [vdso]
+map: start: f77c7000 end: f77d4000 offset: 313000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d4000 end: f77d5000 offset: 320000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d5000 end: f77d6000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7ec6000 end: f7ee7000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: ffe4e000 end: ffe70000 offset: 0 load_bias: 0 flags: 3 name: [stack]
 registers: 348 00000000abdae6fff83b7df704000000a6ec77f7abdae6ff00000000afdae6ff78dae6ff150000001c000000b8f132f7a0f132f7d0df48f7a0ca48f728d9e6ff000000008c8decf78c8decf7ceca48f7a8dae6ff8d8decf70a0000000000000014dae6ff8c8decf78c8decf78c8decf78c8decf78c8decf7a9dae6ff06000000c03a23f78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78d8decf78d8decf7c03a23f7543b23f7000033f75f5f0ff7c03a23f7503423f77000000098000000020000000f2700006c0000000e00000080000000000000008c8decf7000000008c8decf77f03ffff0000ffffffffffff0000000000000000000000000000ffff040000000f27000008000000003023f78d8decf7f069ec000046bdaa308decf715537df7f83b7df78c8decf74c1c71f78c8decf715537df70000000000000000f069ecf7f83b7df75064ecf792e671f7f069ecf7
 stack: start: f732c000 end: f7330000 size: 16384 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d9d49f761b009f71e382ff7000000000000000000000000000000000000000002000000f6372ff704c82bf70000000000204bf7f0a908f7ceca48f728d9e6fff4a449f70000000020f332f720f332f7d0df48f7f8f132f794c748f720f332f70c144205978142a8d4be08f7d0df48f720f332f7a0ca48f71c000000150000000e000000070000001c000000a0ca48f7d0df48f748f232f739c848f7070000000e000000150000001c000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7100000000c000000080000000400000010000000a0ca48f7d0df48f798f232f7c9c848f704000000080000000c00000010000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f70800000006000000040000000200000008000000a0ca48f7d0df48f7e8f232f759c948f702000000040000000600000008000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7040000000300000002000000010000000046bdaa00000000d0df48f738f332f77cca48f701000000020000000300000004000000a0ca48f720f332f700000000f83b7df7696d4df7d0df48f788dae6ff28d9e6ff28d9e6ff88dae6ff58f332f70046bdaa40fb32f7f83b7df758f332f78d6d4df728d9e6ff88dae6fff83b7df728d9e6ff28d9e6ff009030f728f432f7726f2ff728d9e6ff40fb32f740fb32f740fb32f790f332f700000000000000000000000000000000000000000000000000000000009030f740fb32f7000f3d0028f432f703b12c75032f144e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a06e2ff700000000000f3d00000000008e3f17f740fb32f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03823f7c4fd32f700000000e0341df7e02e1df7e03d1df70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040fb32f7188eecf740fb32f70100000030647cf70046bdaa28658876000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a705f7a4b130f7f2860000f1860000b0fb32f7ecffffff000000000000000090f332f7000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ccfb32f70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c2638cfa7f3e1d0000000000000000000000000000000000406d4df728d9e6ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c032f7004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 function: start: 0 end: f748c740 name: unknown_start
diff --git a/libbacktrace/testdata/x86_64/offline_testdata b/libbacktrace/testdata/x86_64/offline_testdata
index baf6450..f8d0dc0 100644
--- a/libbacktrace/testdata/x86_64/offline_testdata
+++ b/libbacktrace/testdata/x86_64/offline_testdata
@@ -1,86 +1,86 @@
 pid: 25683 tid: 25692
-map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_base: 0 flags: 3 name: [heap]
-map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_base: 0 flags: 3 name: [stack]
-map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_base: ffffffffff700000 flags: 5 name: [vdso]
-map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_base: 0 flags: 5 name: [vsyscall]
+map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_bias: ffffffffff700000 flags: 5 name: [vdso]
+map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_bias: 0 flags: 5 name: [vsyscall]
 registers: 936 010000000000000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f0000b92455add57f0000b07bcfabd57f000098deb6abd57f0000b82455add57f0000010000000000000000000000000000000000000000000000c0e354add57f000000e7b6abd57f0000c8b080f4fc7f00000e0000000000000080ddb6abd57f000000000000000000001500000000000000b07bcfabd57f00001c0000000000000060ddb6abd57f0000d07bcfabd57f0000a0b180f4fc7f000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f000078b480f4fc7f000078b480f4fc7f00007f03ffff0000ffffffffffff000000000000000000000000801f0000fc7f000078b480f4fc7f000060b280f4fc7f000000006a80f3f73cf110b480f4fc7f0000de66d6abd57f00000000000000000000000000000000000000006a80f3f73cf128b480f4fc7f0000782455add57f0000fd96d6abd57f0000092555add57f0000000000000000000057b480f4fc7f0000092555add57f000060b480f4fc7f00006994d6abd57f0000b0e0c6abd57f000071b280f4fc7f000070b280f4fc7f0000256c640000000000a8b180f4fc7f000090b480f4fc7f0000082555add57f0000092555add57f0000092555add57f0000082555add57f00001700000000000000082555add57f0000082555add57f0000f93cfcabd57f0000092555add57f000016000000000000000000000000000000090c07acd57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f000010b380f4fc7f000078b480f4fc7f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000092555add57f0000092555add57f0000b92455add57f000028b480f4fc7f0000c800000000000000014c7f0b0380ffff0d000000fc7f0000030000000000000033000000d57f000000b480f4fc7f00000000000000000000a0e154ad5b000000000000000000000000000000000000006e000000770000000000000000000000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f00006027b4aad57f0000c800000000000000a0e154add57f0000c0e354add57f0000092555add57f000000006a80f3f73cf1e0e054add57f0000e0e554add57f0000d14df6abd57f0000
 stack: start: 7fd5abb6b000 end: 7fd5abb6f000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c4eaeabd57f00000000000000000000978142a8000000000f0000000000000012000000000000003888b4abd57f0000e657aeabd57f00000000000000000000a0dcb6abd57f0000307d78aad57f0000b0ddb6abd57f000028d978aad57f0000060aa10200000000a0ddb6abd57f0000000000000000000000000000000000002091b1abd57f0000e094b4abd57f000017008cabd57f0000c84d79aad57f000028e28babd57f00000000000005000000d503000001000000000000000000000068deb6abd57f000040deb6abd57f00002091b1abd57f00000100000000000000e0f3c6abd57f000088f0c6abd57f00006159aeabd57f000000000000000000002091b1abd57f000005000000000000000000000000000000010000000000000088f0c6abd57f00000000000000000000d07bcfabd57f00000000000000000000000000000000000098deb6abd57f000098deb6abd57f0000b0ddb6abd57f00006179cfabd57f000098deb6abd57f0000b07bcfabd57f00001c000000150000000e00000007000000f0ddb6abd57f0000e179cfabd57f00000000000000000000150000001c00000098deb6abd57f0000b07bcfabd57f0000100000000c000000080000000400000030deb6abd57f0000417acfabd57f000000000000000000000c0000001000000098deb6abd57f0000b07bcfabd57f00000800000006000000040000000200000070deb6abd57f0000a17acfabd57f00000000000000000000060000000800000098deb6abd57f0000b07bcfabd57f000004000000030000000200000001000000b0deb6abd57f0000817bcfabd57f0000000000000000000074b480f4fc7f0000c8b080f4fc7f0000c8b080f4fc7f000074b480f4fc7f000000006a80f3f73cf1d0deb6abd57f00002a52d5abd57f0000c8b080f4fc7f0000c8b080f4fc7f0000000000000000000084518cabd57f0000000000000000000000e7b6abd57f000000e7b6abd57f00008f990b1e3bad5a6700000000000000000000000000000000c0e354add57f000000e7b6abd57f00008f99cba356faf1988f9991bc23faf1980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f00007de387aad57f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006030b4aad57f0000b8edb6abd57f00000000000000000000000000000000000080b88eaad57f0000000000000000000080b28eaad57f0000000000000000000080c18eaad57f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f0000402555add57f000000e7b6abd57f00000100000000000000000000000000000000006a80f3f73cf1058f9d56adb3c7cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000407ab1abd57f000030a3adabd57f00005c64000053640000e0e9b6abd57f0000e0e9b6abd57f0000e0ffffffffffffff00000000000000000000000000000000f0deb6abd57f00000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010eab6abd57f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c46ad90f52391d00000000000000000000000000000000000000000000000000f051d5abd57f0000c8b080f4fc7f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0b6abd57f000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 function: start: 0 end: 7fd5abcf7930 name: unknown_start
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 4a5f2a7..47de12a 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,9 @@
 cc_library {
     name: "libcrypto_utils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     host_supported: true,
     srcs: [
         "android_pubkey.c",
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 245deb1..cfe8d29 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -50,6 +50,10 @@
 cc_library {
     name: "libcutils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     host_supported: true,
     srcs: [
         "config_utils.c",
@@ -150,14 +154,13 @@
         "libutils_headers",
     ],
     export_header_lib_headers: ["libcutils_headers"],
+    local_include_dirs: ["include"],
 
     cflags: [
         "-Werror",
         "-Wall",
         "-Wextra",
     ],
-
-    clang: true,
 }
 
 subdirs = ["tests"]
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index a33e45f..996d89d 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -35,11 +35,11 @@
             restart_cmd = "shutdown";
             break;
         case ANDROID_RB_THERMOFF:
-            restart_cmd = "thermal-shutdown";
+            restart_cmd = "shutdown,thermal";
             break;
     }
     if (!restart_cmd) return -1;
-    if (arg) {
+    if (arg && arg[0]) {
         ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
     } else {
         ret = asprintf(&prop_value, "%s", restart_cmd);
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index b4abb79..95f2259 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -51,7 +51,7 @@
     int ret;
     struct stat st;
 
-    int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
+    int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
     if (fd < 0) {
         return fd;
     }
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index 96ca566..819a846 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -23,6 +23,7 @@
 #include <string.h>
 
 #include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
 #include <private/canned_fs_config.h>
 
 typedef struct {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index c39071c..7603ffc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -22,7 +22,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -80,7 +79,6 @@
     { 00775, AID_ROOT,         AID_ROOT,         0, "data/preloads" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
-    { 00755, AID_ROOT,         AID_ROOT,         0, "root" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
@@ -176,6 +174,8 @@
                                            CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/logd" },
+    { 00550, AID_SYSTEM,    AID_LOG,      CAP_MASK_LONG(CAP_SYSLOG),
+                                              "system/bin/bootstat" },
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/run-as" },
@@ -267,14 +267,27 @@
     return false;
 }
 
+static inline bool prefix_cmp(bool partial, const char* prefix, size_t len, const char* path,
+                              size_t plen) {
+    return ((partial && plen >= len) || (plen == len)) && !strncmp(prefix, path, len);
+}
+
 // alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
 // "system/<partition>/<stuff>" to "<partition>/<stuff>"
-static bool prefix_cmp(const char* prefix, const char* path, size_t len) {
-    if (!strncmp(prefix, path, len)) return true;
+static bool fs_config_cmp(bool partial, const char* prefix, size_t len, const char* path,
+                          size_t plen) {
+    // If name ends in * then allow partial matches.
+    if (!partial && prefix[len - 1] == '*') {
+        len--;
+        partial = true;
+    }
+
+    if (prefix_cmp(partial, prefix, len, path, plen)) return true;
 
     static const char system[] = "system/";
     if (!strncmp(path, system, strlen(system))) {
         path += strlen(system);
+        plen -= strlen(system);
     } else if (len <= strlen(system)) {
         return false;
     } else if (strncmp(prefix, system, strlen(system))) {
@@ -283,25 +296,11 @@
         prefix += strlen(system);
         len -= strlen(system);
     }
-    return is_partition(prefix, len) && !strncmp(prefix, path, len);
+    return is_partition(prefix, len) && prefix_cmp(partial, prefix, len, path, plen);
 }
-
-static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
-    if (dir) {
-        if (plen < len) {
-            return false;
-        }
-    } else {
-        // If name ends in * then allow partial matches.
-        if (prefix[len - 1] == '*') {
-            return prefix_cmp(prefix, path, len - 1);
-        }
-        if (plen != len) {
-            return false;
-        }
-    }
-    return prefix_cmp(prefix, path, len);
-}
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__fs_config_cmp = fs_config_cmp;
+#endif
 
 void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
                unsigned* mode, uint64_t* capabilities) {
diff --git a/libcutils/include/cutils/android_filesystem_config.h b/libcutils/include/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..d2a92fe
--- /dev/null
+++ b/libcutils/include/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 716567a..a903adb 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -29,8 +29,8 @@
 /* Properties */
 #define ANDROID_RB_PROPERTY "sys.powerctl"
 
-/* Android reboot reason stored in this file */
-#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"
+/* Android reboot reason stored in this property */
+#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
 
 /* Reboot or shutdown the system.
  * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
diff --git a/libcutils/include/cutils/list.h b/libcutils/include/cutils/list.h
index 4ba2cfd..dfdc53b 100644
--- a/libcutils/include/cutils/list.h
+++ b/libcutils/include/cutils/list.h
@@ -34,20 +34,20 @@
 
 #define list_declare(name) \
     struct listnode name = { \
-        .next = &name, \
-        .prev = &name, \
+        .next = &(name), \
+        .prev = &(name), \
     }
 
 #define list_for_each(node, list) \
-    for (node = (list)->next; node != (list); node = node->next)
+    for ((node) = (list)->next; (node) != (list); (node) = (node)->next)
 
 #define list_for_each_reverse(node, list) \
-    for (node = (list)->prev; node != (list); node = node->prev)
+    for ((node) = (list)->prev; (node) != (list); (node) = (node)->prev)
 
 #define list_for_each_safe(node, n, list) \
-    for (node = (list)->next, n = node->next; \
-         node != (list); \
-         node = n, n = node->next)
+    for ((node) = (list)->next, (n) = (node)->next; \
+         (node) != (list); \
+         (node) = (n), (n) = (node)->next)
 
 static inline void list_init(struct listnode *node)
 {
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index abe6dd6..10f5bc0 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -25,8 +25,8 @@
 
 /* Declare a char array for use with native_handle_init */
 #define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
-    alignas(native_handle_t) char name[                            \
-      sizeof(native_handle_t) + sizeof(int) * (maxFds + maxInts)]
+    alignas(native_handle_t) char (name)[                            \
+      sizeof(native_handle_t) + sizeof(int) * ((maxFds) + (maxInts))]
 
 typedef struct native_handle
 {
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index b45f58f..d2e0871 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -43,12 +43,7 @@
 ** If the property read fails or returns an empty value, the default
 ** value is used (if nonnull).
 */
-int property_get(const char *key, char *value, const char *default_value)
-/* Sometimes we use not-Bionic with this, so we need this check. */
-#if defined(__BIONIC_FORTIFY)
-        __overloadable __RENAME_CLANG(property_get)
-#endif
-        ;
+int property_get(const char* key, char* value, const char* default_value);
 
 /* property_get_bool: returns the value of key coerced into a
 ** boolean. If the property is not set, then the default value is returned.
@@ -119,27 +114,15 @@
 
 #if defined(__clang__)
 
-/* Some projects use -Weverything; enable_if is clang-specific.
-** FIXME: This is marked used because we'll otherwise get complaints about an
-** unused static function. This is more robust than marking it unused, since
-** -Wused-but-marked-unused is a thing that will complain if this function is
-** actually used, thus making FORTIFY noisier when an error happens. It's going
-** to go away anyway during our FORTIFY cleanup.
-**/
+/* Some projects use -Weverything; diagnose_if is clang-specific. */
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wgcc-compat"
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-int property_get(const char *key, char *value, const char *default_value)
-        __overloadable
-        __enable_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
-                    __bos(value) < PROPERTY_VALUE_MAX, __property_get_err_str)
-        __errorattr(__property_get_err_str)
-        __attribute__((used));
+int property_get(const char* key, char* value, const char* default_value)
+    __clang_error_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+                         __bos(value) < PROPERTY_VALUE_MAX,
+                     __property_get_err_str);
 #pragma clang diagnostic pop
 
-/* No object size? No FORTIFY.
-*/
-
 #else /* defined(__clang__) */
 
 extern int __property_get_real(const char *, char *, const char *)
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 9683f91..4c1113b 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -34,7 +34,7 @@
  * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
  * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
  *
- * Return value: 1 if Linux kernel CONFIG_SCHEDTUNE=y; 0 otherwise.
+ * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
  */
 extern bool schedboost_enabled();
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 02141d6..55ece54 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-/* This file is used to define the properties of the filesystem
-** images generated by build tools (mkbootfs and mkyaffs2image) and
-** by the device side of adb.
-*/
-
 /*
  * This file is consumed by build/tools/fs_config and is used
  * for generating various files. Anything #define AID_<name>
@@ -49,18 +44,12 @@
 #ifndef _ANDROID_FILESYSTEM_CONFIG_H_
 #define _ANDROID_FILESYSTEM_CONFIG_H_
 
-#include <stdint.h>
-#include <sys/cdefs.h>
 #include <sys/types.h>
 
-#if defined(__BIONIC__)
-#include <linux/capability.h>
-#else
-#include "android_filesystem_capability.h"
+#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+#include <private/fs_config.h>
 #endif
 
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
 /* This is the master Users and Groups config for the platform.
  * DO NOT EVER RENUMBER
  */
@@ -131,6 +120,8 @@
 #define AID_ESE 1060             /* embedded secure element (eSE) subsystem */
 #define AID_OTA_UPDATE 1061      /* resource tracking UID for OTA updates */
 #define AID_AUTOMOTIVE_EVS 1062  /* Automotive rear and surround view system */
+#define AID_LOWPAN 1063          /* LoWPAN subsystem */
+#define AID_HSM 1064             /* hardware security module subsystem */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
@@ -193,36 +184,4 @@
  * Also see build/tools/fs_config for more details.
  */
 
-#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-
-struct fs_path_config {
-    unsigned mode;
-    unsigned uid;
-    unsigned gid;
-    uint64_t capabilities;
-    const char* prefix;
-};
-
-/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
-
-__BEGIN_DECLS
-
-/*
- * Used in:
- *  build/tools/fs_config/fs_config.c
- *  build/tools/fs_get_stats/fs_get_stats.c
- *  system/extras/ext4_utils/make_ext4fs_main.c
- *  external/squashfs-tools/squashfs-tools/android.c
- *  system/core/cpio/mkbootfs.c
- *  system/core/adb/file_sync_service.cpp
- *  system/extras/ext4_utils/canned_fs_config.c
- */
-void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
-               unsigned* mode, uint64_t* capabilities);
-
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
-
-__END_DECLS
-
-#endif
 #endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 7dad668..aab5042 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -14,10 +14,24 @@
  * limitations under the License.
  */
 
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
 #ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
 #define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
 
 #include <stdint.h>
+#include <sys/cdefs.h>
+
+#if defined(__BIONIC__)
+#include <linux/capability.h>
+#else  // defined(__BIONIC__)
+#include "android_filesystem_capability.h"
+#endif  // defined(__BIONIC__)
+
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
 
 /*
  * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
@@ -34,4 +48,33 @@
     char prefix[];
 } __attribute__((__aligned__(sizeof(uint64_t))));
 
+struct fs_path_config {
+    unsigned mode;
+    unsigned uid;
+    unsigned gid;
+    uint64_t capabilities;
+    const char* prefix;
+};
+
+/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
+
+__BEGIN_DECLS
+
+/*
+ * Used in:
+ *  build/tools/fs_config/fs_config.c
+ *  build/tools/fs_get_stats/fs_get_stats.c
+ *  system/extras/ext4_utils/make_ext4fs_main.c
+ *  external/squashfs-tools/squashfs-tools/android.c
+ *  system/core/cpio/mkbootfs.c
+ *  system/core/adb/file_sync_service.cpp
+ *  system/extras/ext4_utils/canned_fs_config.c
+ */
+void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
+               unsigned* mode, uint64_t* capabilities);
+
+ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
+
+__END_DECLS
+
 #endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libcutils/include_vndk/cutils/android_filesystem_config.h b/libcutils/include_vndk/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..13a5a08
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../../include/private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/private b/libcutils/include_vndk/private
deleted file mode 120000
index 2245a85..0000000
--- a/libcutils/include_vndk/private
+++ /dev/null
@@ -1 +0,0 @@
-../include/private
\ No newline at end of file
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index d2645e6..25ff1a3 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -21,7 +21,6 @@
 #include <ctype.h>
 #include <errno.h>
 #include <inttypes.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 7170b48..b00fa85 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -28,13 +28,6 @@
 
 #define UNUSED __attribute__((__unused__))
 
-#ifndef SLOGE
-#define SLOGE ALOGE
-#endif
-#ifndef SLOGW
-#define SLOGW ALOGW
-#endif
-
 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
  * Call this any place a SchedPolicy is used as an input parameter.
  * Returns the possibly re-mapped policy.
@@ -124,11 +117,8 @@
     on where init.rc mounts cpuset. That's why we'd better require this
     configuration be set if CONFIG_CPUSETS is set.
 
-    With runtime check using the following function, build time
-    variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
-    in Android.bp) are not needed.
+    In older releases, this was controlled by build-time configuration.
  */
-
 bool cpusets_enabled() {
     static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
 
@@ -137,15 +127,11 @@
 
 /*
     Similar to CONFIG_CPUSETS above, but with a different configuration
-    CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
+    CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro
     Stable Kernel (LSK), but not in mainline Linux as of v4.9.
 
-    With runtime check using the following function, build time
-    variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
-    (used in Android.bp) are not needed.
-
+    In older releases, this was controlled by build-time configuration.
  */
-
 bool schedboost_enabled() {
     static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
 
@@ -343,7 +329,7 @@
     return 0;
 }
 
-static void set_timerslack_ns(int tid, unsigned long long slack) {
+static void set_timerslack_ns(int tid, unsigned long slack) {
     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
     // TODO: once we've backported this, log if the open(2) fails.
     if (__sys_supports_timerslack) {
@@ -351,7 +337,7 @@
         snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
         int fd = open(buf, O_WRONLY | O_CLOEXEC);
         if (fd != -1) {
-            int len = snprintf(buf, sizeof(buf), "%llu", slack);
+            int len = snprintf(buf, sizeof(buf), "%lu", slack);
             if (write(fd, buf, len) != len) {
                 SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
             }
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.c
index 37851b1..1b87c49 100644
--- a/libcutils/socket_network_client_unix.c
+++ b/libcutils/socket_network_client_unix.c
@@ -63,7 +63,7 @@
     for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
         // The Mac doesn't have SOCK_NONBLOCK.
         int s = socket(addr->ai_family, type, addr->ai_protocol);
-        if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+        if (s == -1 || toggle_O_NONBLOCK(s) == -1) break;
 
         int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
         if (rc == 0) {
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index a62cd51..391adb6 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -29,12 +29,39 @@
 
 extern const fs_path_config* __for_testing_only__android_dirs;
 extern const fs_path_config* __for_testing_only__android_files;
+extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);
 
 // Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
 // hit a nullptr termination, before we declare the list is just too big or
 // could be missing the nullptr.
 static constexpr size_t max_idx = 4096;
 
+static const struct fs_config_cmp_test {
+    bool dir;
+    const char* prefix;
+    const char* path;
+    bool match;
+} fs_config_cmp_tests[] = {
+    // clang-format off
+    { true,  "system/lib",             "system/lib/hw",           true  },
+    { true,  "vendor/lib",             "system/vendor/lib/hw",    true  },
+    { true,  "system/vendor/lib",      "vendor/lib/hw",           true  },
+    { true,  "system/vendor/lib",      "system/vendor/lib/hw",    true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/w",     false },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi",  true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi2", false },
+    { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi",  true, },
+    { false, "odm/bin/wifi",           "system/odm/bin/wifi",     true  },
+    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     true  },
+    { false, "data/bin/wifi",          "system/data/bin/wifi",    false },
+    { false, "system/bin/*",           "system/bin/wifi",         true  },
+    { false, "vendor/bin/*",           "system/vendor/bin/wifi",  true  },
+    { false, "system/bin/*",           "system/bin",              false },
+    { false, "system/vendor/bin/*",    "vendor/bin/wifi",         true  },
+    { false, NULL,                     NULL,                      false },
+    // clang-format on
+};
+
 static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
                          const std::string& prefix) {
     bool retval = false;
@@ -106,6 +133,22 @@
     return check_unique(paths_tmp, config, prefix) || retval;
 }
 
+static bool check_fs_config_cmp(const fs_config_cmp_test* tests) {
+    bool match, retval = false;
+    for (size_t idx = 0; tests[idx].prefix; ++idx) {
+        match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,
+                                                  strlen(tests[idx].prefix), tests[idx].path,
+                                                  strlen(tests[idx].path));
+        if (match != tests[idx].match) {
+            GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ")
+                              << tests[idx].prefix;
+            retval = true;
+            break;
+        }
+    }
+    return retval;
+}
+
 #define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
 
 static bool check_unique(const std::string& config, const std::string& prefix) {
@@ -199,3 +242,7 @@
 TEST(fs_config, odm_files_alias) {
     check_two(__for_testing_only__android_files, "files", "odm/");
 }
+
+TEST(fs_config, system_alias) {
+    EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));
+}
diff --git a/libcutils/trace-container.c b/libcutils/trace-container.c
new file mode 100644
index 0000000..03e91b1
--- /dev/null
+++ b/libcutils/trace-container.c
@@ -0,0 +1,231 @@
+/*
+ * 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 "trace-dev.inc"
+
+#include <cutils/sockets.h>
+#include <sys/stat.h>
+#include <time.h>
+
+/**
+ * For tracing in container, tags are written into a socket
+ * instead of ftrace. Additional data is appended so we need extra space.
+ */
+#define CONTAINER_ATRACE_MESSAGE_LENGTH (ATRACE_MESSAGE_LENGTH + 512)
+
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
+
+// Variables used for tracing in container with socket.
+// Note that we need to manually close and reopen socket when Zygote is forking. This requires
+// writing and closing sockets on multiple threads. A rwlock is used for avoiding concurrent
+// operation on the file descriptor.
+static bool             atrace_use_container_sock    = false;
+static int              atrace_container_sock_fd     = -1;
+static pthread_mutex_t  atrace_enabling_mutex        = PTHREAD_MUTEX_INITIALIZER;
+static pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+static bool atrace_init_container_sock()
+{
+    pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+    atrace_container_sock_fd =
+        socket_local_client("trace", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+    if (atrace_container_sock_fd < 0) {
+        ALOGE("Error opening container trace socket: %s (%d)", strerror(errno), errno);
+    }
+    pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+    return atrace_container_sock_fd != -1;
+}
+
+static void atrace_close_container_sock()
+{
+    pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+    if (atrace_container_sock_fd != -1) close(atrace_container_sock_fd);
+    atrace_container_sock_fd = -1;
+    pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+}
+
+// Set whether tracing is enabled in this process.  This is used to prevent
+// the Zygote process from tracing.  We need to close the socket in the container when tracing is
+// disabled, and reopen it again after Zygote forking.
+void atrace_set_tracing_enabled(bool enabled)
+{
+    pthread_mutex_lock(&atrace_enabling_mutex);
+    if (atrace_use_container_sock) {
+        bool already_enabled = atomic_load_explicit(&atrace_is_enabled, memory_order_acquire);
+        if (enabled && !already_enabled) {
+            // Trace was disabled previously. Re-initialize container socket.
+            atrace_init_container_sock();
+        } else if (!enabled && already_enabled) {
+            // Trace was enabled previously. Close container socket.
+            atrace_close_container_sock();
+        }
+    }
+    atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
+    pthread_mutex_unlock(&atrace_enabling_mutex);
+    atrace_update_tags();
+}
+
+static void atrace_init_once()
+{
+    atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+    if (atrace_marker_fd < 0) {
+        // We're in container, ftrace may be disabled. In such case, we use the
+        // socket to write trace event.
+
+        // Protect the initialization of container socket from
+        // atrace_set_tracing_enabled.
+        pthread_mutex_lock(&atrace_enabling_mutex);
+        atrace_use_container_sock = true;
+        bool success = false;
+        if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+            success = atrace_init_container_sock();
+        }
+        pthread_mutex_unlock(&atrace_enabling_mutex);
+
+        if (!success) {
+            atrace_enabled_tags = 0;
+            goto done;
+        }
+    }
+    atrace_enabled_tags = atrace_get_property();
+
+done:
+    atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+}
+
+void atrace_setup()
+{
+    pthread_once(&atrace_once_control, atrace_init_once);
+}
+
+static inline uint64_t gettime(clockid_t clk_id)
+{
+    struct timespec ts;
+    clock_gettime(clk_id, &ts);
+    return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+}
+
+// Write trace events to container trace file. Note that we need to amend tid and time information
+// here comparing to normal ftrace, where those informations are added by kernel.
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
+    char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
+    int pid = getpid(); \
+    int tid = gettid(); \
+    uint64_t ts = gettime(CLOCK_MONOTONIC); \
+    uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
+    int len = snprintf( \
+            buf, sizeof(buf), \
+            ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
+            pid, tid, ts, tts, name, value); \
+    if (len >= (int) sizeof(buf)) { \
+        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+        /* Truncate the name to make the message fit. */ \
+        if (name_len > 0) { \
+            ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+            len = snprintf( \
+                    buf, sizeof(buf), \
+                    ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
+                    pid, tid, ts, tts, name_len, name, value); \
+        } else { \
+            /* Data is still too long. Drop it. */ \
+            ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
+            len = 0; \
+        } \
+    } \
+    if (len > 0) { \
+        write(atrace_container_sock_fd, buf, len); \
+    } \
+}
+
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
+    pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
+    if (atrace_container_sock_fd != -1) { \
+       WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
+    } \
+    pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
+}
+
+void atrace_begin_body(const char* name)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("B|%d|", "%s", name, "");
+}
+
+void atrace_end_body()
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("E|%d", "%s", "", "");
+}
+
+void atrace_async_begin_body(const char* name, int32_t cookie)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_async_end_body(const char* name, int32_t cookie)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_int_body(const char* name, int32_t value)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+}
+
+void atrace_int64_body(const char* name, int64_t value)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index d45e5a9..4468e83 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -14,47 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "cutils-trace"
+#include "trace-dev.inc"
 
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <pthread.h>
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <cutils/trace.h>
-#include <log/log.h>
-#include <log/log_properties.h>
-
-/**
- * Maximum size of a message that can be logged to the trace buffer.
- * Note this message includes a tag, the pid, and the string given as the name.
- * Names should be kept short to get the most use of the trace buffer.
- */
-#define ATRACE_MESSAGE_LENGTH 1024
-
-atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(false);
-int                     atrace_marker_fd     = -1;
-uint64_t                atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
-static bool             atrace_is_debuggable = false;
-static atomic_bool      atrace_is_enabled    = ATOMIC_VAR_INIT(true);
-static pthread_once_t   atrace_once_control  = PTHREAD_ONCE_INIT;
-static pthread_mutex_t  atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
-
-// Set whether this process is debuggable, which determines whether
-// application-level tracing is allowed when the ro.debuggable system property
-// is not set to '1'.
-void atrace_set_debuggable(bool debuggable)
-{
-    atrace_is_debuggable = debuggable;
-    atrace_update_tags();
-}
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
 
 // Set whether tracing is enabled in this process.  This is used to prevent
 // the Zygote process from tracing.
@@ -64,101 +26,6 @@
     atrace_update_tags();
 }
 
-// Check whether the given command line matches one of the comma-separated
-// values listed in the app_cmdlines property.
-static bool atrace_is_cmdline_match(const char* cmdline)
-{
-    int count = property_get_int32("debug.atrace.app_number", 0);
-
-    char buf[PROPERTY_KEY_MAX];
-    char value[PROPERTY_VALUE_MAX];
-
-    for (int i = 0; i < count; i++) {
-        snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
-        property_get(buf, value, "");
-        if (strcmp(value, cmdline) == 0) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-// Determine whether application-level tracing is enabled for this process.
-static bool atrace_is_app_tracing_enabled()
-{
-    bool sys_debuggable = __android_log_is_debuggable();
-    bool result = false;
-
-    if (sys_debuggable || atrace_is_debuggable) {
-        // Check whether tracing is enabled for this process.
-        FILE * file = fopen("/proc/self/cmdline", "re");
-        if (file) {
-            char cmdline[4096];
-            if (fgets(cmdline, sizeof(cmdline), file)) {
-                result = atrace_is_cmdline_match(cmdline);
-            } else {
-                ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
-            }
-            fclose(file);
-        } else {
-            ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
-                    errno);
-        }
-    }
-
-    return result;
-}
-
-// Read the sysprop and return the value tags should be set to
-static uint64_t atrace_get_property()
-{
-    char value[PROPERTY_VALUE_MAX];
-    char *endptr;
-    uint64_t tags;
-
-    property_get("debug.atrace.tags.enableflags", value, "0");
-    errno = 0;
-    tags = strtoull(value, &endptr, 0);
-    if (value[0] == '\0' || *endptr != '\0') {
-        ALOGE("Error parsing trace property: Not a number: %s", value);
-        return 0;
-    } else if (errno == ERANGE || tags == ULLONG_MAX) {
-        ALOGE("Error parsing trace property: Number too large: %s", value);
-        return 0;
-    }
-
-    // Only set the "app" tag if this process was selected for app-level debug
-    // tracing.
-    if (atrace_is_app_tracing_enabled()) {
-        tags |= ATRACE_TAG_APP;
-    } else {
-        tags &= ~ATRACE_TAG_APP;
-    }
-
-    return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
-}
-
-// Update tags if tracing is ready. Useful as a sysprop change callback.
-void atrace_update_tags()
-{
-    uint64_t tags;
-    if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
-        if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
-            tags = atrace_get_property();
-            pthread_mutex_lock(&atrace_tags_mutex);
-            atrace_enabled_tags = tags;
-            pthread_mutex_unlock(&atrace_tags_mutex);
-        } else {
-            // Tracing is disabled for this process, so we simply don't
-            // initialize the tags.
-            pthread_mutex_lock(&atrace_tags_mutex);
-            atrace_enabled_tags = ATRACE_TAG_NOT_READY;
-            pthread_mutex_unlock(&atrace_tags_mutex);
-        }
-    }
-}
-
 static void atrace_init_once()
 {
     atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
@@ -181,54 +48,30 @@
 
 void atrace_begin_body(const char* name)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-
-    int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
-    if (len >= (int) sizeof(buf)) {
-        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
-        len = sizeof(buf) - 1;
-    }
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("B|%d|", "%s", name, "");
 }
 
 void atrace_end_body()
 {
-    char c = 'E';
-    write(atrace_marker_fd, &c, 1);
-}
-
-#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
-    char buf[ATRACE_MESSAGE_LENGTH]; \
-    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
-        name, value); \
-    if (len >= (int) sizeof(buf)) { \
-        /* Given the sizeof(buf), and all of the current format buffers, \
-         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
-        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
-        /* Truncate the name to make the message fit. */ \
-        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
-        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
-            name_len, name, value); \
-    } \
-    write(atrace_marker_fd, buf, len); \
+    WRITE_MSG("E|%d", "%s", "", "");
 }
 
 void atrace_async_begin_body(const char* name, int32_t cookie)
 {
-    WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
+    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
 }
 
 void atrace_async_end_body(const char* name, int32_t cookie)
 {
-    WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
+    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
 }
 
 void atrace_int_body(const char* name, int32_t value)
 {
-    WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
+    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
 }
 
 void atrace_int64_body(const char* name, int64_t value)
 {
-    WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
+    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
 }
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
new file mode 100644
index 0000000..f32330a
--- /dev/null
+++ b/libcutils/trace-dev.inc
@@ -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.
+ */
+
+#ifndef __TRACE_DEV_INC
+#define __TRACE_DEV_INC
+
+#define LOG_TAG "cutils-trace"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <log/log.h>
+#include <log/log_properties.h>
+
+/**
+ * Maximum size of a message that can be logged to the trace buffer.
+ * Note this message includes a tag, the pid, and the string given as the name.
+ * Names should be kept short to get the most use of the trace buffer.
+ */
+#define ATRACE_MESSAGE_LENGTH 1024
+
+atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(false);
+int                     atrace_marker_fd     = -1;
+uint64_t                atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
+static bool             atrace_is_debuggable = false;
+static atomic_bool      atrace_is_enabled    = ATOMIC_VAR_INIT(true);
+static pthread_mutex_t  atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
+
+// Set whether this process is debuggable, which determines whether
+// application-level tracing is allowed when the ro.debuggable system property
+// is not set to '1'.
+void atrace_set_debuggable(bool debuggable)
+{
+    atrace_is_debuggable = debuggable;
+    atrace_update_tags();
+}
+
+// Check whether the given command line matches one of the comma-separated
+// values listed in the app_cmdlines property.
+static bool atrace_is_cmdline_match(const char* cmdline)
+{
+    int count = property_get_int32("debug.atrace.app_number", 0);
+
+    char buf[PROPERTY_KEY_MAX];
+    char value[PROPERTY_VALUE_MAX];
+
+    for (int i = 0; i < count; i++) {
+        snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
+        property_get(buf, value, "");
+        if (strcmp(value, cmdline) == 0) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+// Determine whether application-level tracing is enabled for this process.
+static bool atrace_is_app_tracing_enabled()
+{
+    bool sys_debuggable = __android_log_is_debuggable();
+    bool result = false;
+
+    if (sys_debuggable || atrace_is_debuggable) {
+        // Check whether tracing is enabled for this process.
+        FILE * file = fopen("/proc/self/cmdline", "re");
+        if (file) {
+            char cmdline[4096];
+            if (fgets(cmdline, sizeof(cmdline), file)) {
+                result = atrace_is_cmdline_match(cmdline);
+            } else {
+                ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
+            }
+            fclose(file);
+        } else {
+            ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
+                    errno);
+        }
+    }
+
+    return result;
+}
+
+// Read the sysprop and return the value tags should be set to
+static uint64_t atrace_get_property()
+{
+    char value[PROPERTY_VALUE_MAX];
+    char *endptr;
+    uint64_t tags;
+
+    property_get("debug.atrace.tags.enableflags", value, "0");
+    errno = 0;
+    tags = strtoull(value, &endptr, 0);
+    if (value[0] == '\0' || *endptr != '\0') {
+        ALOGE("Error parsing trace property: Not a number: %s", value);
+        return 0;
+    } else if (errno == ERANGE || tags == ULLONG_MAX) {
+        ALOGE("Error parsing trace property: Number too large: %s", value);
+        return 0;
+    }
+
+    // Only set the "app" tag if this process was selected for app-level debug
+    // tracing.
+    if (atrace_is_app_tracing_enabled()) {
+        tags |= ATRACE_TAG_APP;
+    } else {
+        tags &= ~ATRACE_TAG_APP;
+    }
+
+    return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
+}
+
+// Update tags if tracing is ready. Useful as a sysprop change callback.
+void atrace_update_tags()
+{
+    uint64_t tags;
+    if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+        if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+            tags = atrace_get_property();
+            pthread_mutex_lock(&atrace_tags_mutex);
+            atrace_enabled_tags = tags;
+            pthread_mutex_unlock(&atrace_tags_mutex);
+        } else {
+            // Tracing is disabled for this process, so we simply don't
+            // initialize the tags.
+            pthread_mutex_lock(&atrace_tags_mutex);
+            atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+            pthread_mutex_unlock(&atrace_tags_mutex);
+        }
+    }
+}
+
+#define WRITE_MSG(format_begin, format_end, name, value) { \
+    char buf[ATRACE_MESSAGE_LENGTH]; \
+    int pid = getpid(); \
+    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+        name, value); \
+    if (len >= (int) sizeof(buf)) { \
+        /* Given the sizeof(buf), and all of the current format buffers, \
+         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+        /* Truncate the name to make the message fit. */ \
+        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+            name_len, name, value); \
+    } \
+    write(atrace_marker_fd, buf, len); \
+}
+
+#endif  // __TRACE_DEV_INC
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 088981a..b92f086 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,6 +1,9 @@
 cc_library {
     name: "libdiskconfig",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     srcs: [
         "diskconfig.c",
         "diskutils.c",
@@ -20,7 +23,7 @@
         darwin: {
             enabled: false,
         },
-        linux: {
+        linux_glibc: {
             cflags: [
                 "-O2",
                 "-g",
diff --git a/libion/Android.bp b/libion/Android.bp
index 6f267e4..6d9fae0 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,7 +1,11 @@
 
 cc_library {
     name: "libion",
-		vendor_available: true,
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     srcs: ["ion.c"],
     shared_libs: ["liblog"],
     local_include_dirs: [
diff --git a/libion/include/ion/ion.h b/libion/include/ion/ion.h
index f47793d..a60d24e 100644
--- a/libion/include/ion/ion.h
+++ b/libion/include/ion/ion.h
@@ -41,6 +41,15 @@
 int ion_share(int fd, ion_user_handle_t handle, int *share_fd);
 int ion_import(int fd, int share_fd, ion_user_handle_t *handle);
 
+/**
+  * Add 4.12+ kernel ION interfaces here for forward compatibility
+  * This should be needed till the pre-4.12+ ION interfaces are backported.
+  */
+int ion_query_heap_cnt(int fd, int* cnt);
+int ion_query_get_heaps(int fd, int cnt, void* buffers);
+
+int ion_is_legacy(int fd);
+
 __END_DECLS
 
 #endif /* __SYS_CORE_ION_H */
diff --git a/libion/ion.c b/libion/ion.c
index 9aaa6f2..5836128 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/ion.h>
+#include <stdatomic.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/ioctl.h>
@@ -30,81 +31,89 @@
 #include <unistd.h>
 
 #include <ion/ion.h>
+#include "ion_4.12.h"
+
 #include <log/log.h>
 
-int ion_open()
-{
+enum ion_version { ION_VERSION_UNKNOWN, ION_VERSION_MODERN, ION_VERSION_LEGACY };
+
+static atomic_int g_ion_version = ATOMIC_VAR_INIT(ION_VERSION_UNKNOWN);
+
+int ion_is_legacy(int fd) {
+    int version = atomic_load_explicit(&g_ion_version, memory_order_acquire);
+    if (version == ION_VERSION_UNKNOWN) {
+        /**
+          * Check for FREE IOCTL here; it is available only in the old
+          * kernels, not the new ones.
+          */
+        int err = ion_free(fd, (ion_user_handle_t)NULL);
+        version = (err == -ENOTTY) ? ION_VERSION_MODERN : ION_VERSION_LEGACY;
+        atomic_store_explicit(&g_ion_version, version, memory_order_release);
+    }
+    return version == ION_VERSION_LEGACY;
+}
+
+int ion_open() {
     int fd = open("/dev/ion", O_RDONLY | O_CLOEXEC);
-    if (fd < 0)
-        ALOGE("open /dev/ion failed!\n");
+    if (fd < 0) ALOGE("open /dev/ion failed!\n");
+
     return fd;
 }
 
-int ion_close(int fd)
-{
+int ion_close(int fd) {
     int ret = close(fd);
-    if (ret < 0)
-        return -errno;
+    if (ret < 0) return -errno;
     return ret;
 }
 
-static int ion_ioctl(int fd, int req, void *arg)
-{
+static int ion_ioctl(int fd, int req, void* arg) {
     int ret = ioctl(fd, req, arg);
     if (ret < 0) {
-        ALOGE("ioctl %x failed with code %d: %s\n", req,
-              ret, strerror(errno));
+        ALOGE("ioctl %x failed with code %d: %s\n", req, ret, strerror(errno));
         return -errno;
     }
     return ret;
 }
 
-int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
-              unsigned int flags, ion_user_handle_t *handle)
-{
-    int ret;
+int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, unsigned int flags,
+              ion_user_handle_t* handle) {
+    int ret = 0;
+
+    if ((handle == NULL) || (!ion_is_legacy(fd))) return -EINVAL;
+
     struct ion_allocation_data data = {
-        .len = len,
-        .align = align,
-        .heap_id_mask = heap_mask,
-        .flags = flags,
+        .len = len, .align = align, .heap_id_mask = heap_mask, .flags = flags,
     };
 
-    if (handle == NULL)
-        return -EINVAL;
-
     ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
-    if (ret < 0)
-        return ret;
+    if (ret < 0) return ret;
+
     *handle = data.handle;
+
     return ret;
 }
 
-int ion_free(int fd, ion_user_handle_t handle)
-{
+int ion_free(int fd, ion_user_handle_t handle) {
     struct ion_handle_data data = {
         .handle = handle,
     };
     return ion_ioctl(fd, ION_IOC_FREE, &data);
 }
 
-int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot,
-            int flags, off_t offset, unsigned char **ptr, int *map_fd)
-{
+int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot, int flags, off_t offset,
+            unsigned char** ptr, int* map_fd) {
+    if (!ion_is_legacy(fd)) return -EINVAL;
     int ret;
-    unsigned char *tmp_ptr;
+    unsigned char* tmp_ptr;
     struct ion_fd_data data = {
         .handle = handle,
     };
 
-    if (map_fd == NULL)
-        return -EINVAL;
-    if (ptr == NULL)
-        return -EINVAL;
+    if (map_fd == NULL) return -EINVAL;
+    if (ptr == NULL) return -EINVAL;
 
     ret = ion_ioctl(fd, ION_IOC_MAP, &data);
-    if (ret < 0)
-        return ret;
+    if (ret < 0) return ret;
     if (data.fd < 0) {
         ALOGE("map ioctl returned negative fd\n");
         return -EINVAL;
@@ -119,19 +128,17 @@
     return ret;
 }
 
-int ion_share(int fd, ion_user_handle_t handle, int *share_fd)
-{
+int ion_share(int fd, ion_user_handle_t handle, int* share_fd) {
     int ret;
     struct ion_fd_data data = {
         .handle = handle,
     };
 
-    if (share_fd == NULL)
-        return -EINVAL;
+    if (!ion_is_legacy(fd)) return -EINVAL;
+    if (share_fd == NULL) return -EINVAL;
 
     ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
-    if (ret < 0)
-        return ret;
+    if (ret < 0) return ret;
     if (data.fd < 0) {
         ALOGE("share ioctl returned negative fd\n");
         return -EINVAL;
@@ -140,40 +147,75 @@
     return ret;
 }
 
-int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask,
-                 unsigned int flags, int *handle_fd) {
+int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, unsigned int flags,
+                 int* handle_fd) {
     ion_user_handle_t handle;
     int ret;
 
-    ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
-    if (ret < 0)
-        return ret;
-    ret = ion_share(fd, handle, handle_fd);
-    ion_free(fd, handle);
+    if (!ion_is_legacy(fd)) {
+        struct ion_new_allocation_data data = {
+            .len = len,
+            .heap_id_mask = heap_mask,
+            .flags = flags,
+        };
+
+        ret = ion_ioctl(fd, ION_IOC_NEW_ALLOC, &data);
+        if (ret < 0) return ret;
+        *handle_fd = data.fd;
+    } else {
+        ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
+        if (ret < 0) return ret;
+        ret = ion_share(fd, handle, handle_fd);
+        ion_free(fd, handle);
+    }
     return ret;
 }
 
-int ion_import(int fd, int share_fd, ion_user_handle_t *handle)
-{
+int ion_import(int fd, int share_fd, ion_user_handle_t* handle) {
     int ret;
     struct ion_fd_data data = {
         .fd = share_fd,
     };
 
-    if (handle == NULL)
-        return -EINVAL;
+    if (!ion_is_legacy(fd)) return -EINVAL;
+
+    if (handle == NULL) return -EINVAL;
 
     ret = ion_ioctl(fd, ION_IOC_IMPORT, &data);
-    if (ret < 0)
-        return ret;
+    if (ret < 0) return ret;
     *handle = data.handle;
     return ret;
 }
 
-int ion_sync_fd(int fd, int handle_fd)
-{
+int ion_sync_fd(int fd, int handle_fd) {
     struct ion_fd_data data = {
         .fd = handle_fd,
     };
+
+    if (!ion_is_legacy(fd)) return -EINVAL;
+
     return ion_ioctl(fd, ION_IOC_SYNC, &data);
 }
+
+int ion_query_heap_cnt(int fd, int* cnt) {
+    int ret;
+    struct ion_heap_query query;
+
+    memset(&query, 0, sizeof(query));
+
+    ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
+    if (ret < 0) return ret;
+
+    *cnt = query.cnt;
+    return ret;
+}
+
+int ion_query_get_heaps(int fd, int cnt, void* buffers) {
+    int ret;
+    struct ion_heap_query query = {
+        .cnt = cnt, .heaps = (uintptr_t)buffers,
+    };
+
+    ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
+    return ret;
+}
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/ion_4.12.h
@@ -0,0 +1,125 @@
+/*
+ * Adapted from drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_new_allocation_data - metadata passed from userspace for allocations
+ * @len:		size of the allocation
+ * @heap_id_mask:	mask of heap ids to allocate from
+ * @flags:		flags passed to heap
+ * @handle:		pointer that will be populated with a cookie to use to
+ *			refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl - added _new to denote
+ * this belongs to the new ION interface.
+ */
+struct ion_new_allocation_data {
+    __u64 len;
+    __u32 heap_id_mask;
+    __u32 flags;
+    __u32 fd;
+    __u32 unused;
+};
+
+#define MAX_HEAP_NAME 32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+    char name[MAX_HEAP_NAME];
+    __u32 type;
+    __u32 heap_id;
+    __u32 reserved0;
+    __u32 reserved1;
+    __u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+    __u32 cnt;       /* Total number of heaps to be copied */
+    __u32 reserved0; /* align to 64bits */
+    __u64 heaps;     /* buffer to be populated */
+    __u32 reserved1;
+    __u32 reserved2;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_NEW_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ * TODO: This IOCTL will clash by design; however, only one of
+ *  ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
+ *  so this should not conflict.
+ */
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ *
+ * #define ION_IOC_FREE		_IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle.  Returns the struct with the fd field set to a file
+ * descriptor open in the current address space.  This file descriptor
+ * can then be passed to another process.  The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ *
+ * #define ION_IOC_SHARE		_IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index 4428848..b3fcb3b 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -16,7 +16,6 @@
 
 cc_test {
     name: "ion-unit-tests",
-    clang: true,
     cflags: [
         "-g",
         "-Wall",
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..d5bb29e 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,24 @@
     "logd_writer.c",
 ]
 
+cc_library_headers {
+    name: "liblog_headers",
+    host_supported: true,
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    target: {
+        windows: {
+            enabled: true,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        vendor: {
+            export_include_dirs: ["include_vndk"],
+        },
+    },
+}
+
 // Shared and static library for host and device
 // ========================================================
 cc_library {
@@ -73,15 +91,13 @@
         not_windows: {
             srcs: ["event_tag_map.cpp"],
         },
-        linux: {
-            host_ldlibs: ["-lrt"],
-        },
         linux_bionic: {
             enabled: true,
         },
     },
 
-    export_include_dirs: ["include"],
+    header_libs: ["liblog_headers"],
+    export_header_lib_headers: ["liblog_headers"],
 
     cflags: [
         "-Werror",
@@ -100,7 +116,7 @@
 }
 
 ndk_headers {
-    name: "liblog_headers",
+    name: "liblog_ndk_headers",
     from: "include/android",
     to: "android",
     srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 3a215e9..d01708d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -161,7 +161,7 @@
 #endif
 
 #if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-clockid_t android_log_clockid();
+clockid_t android_log_clockid(void);
 #endif
 
 #endif /* __linux__ */
@@ -185,7 +185,7 @@
  * May be used to clean up File descriptors after a Fork, the resources are
  * all O_CLOEXEC so wil self clean on exec().
  */
-void __android_log_close();
+void __android_log_close(void);
 #endif
 
 #ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index da16158..339a06d 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -18,10 +18,9 @@
 #define _LIBS_LOG_LOG_MAIN_H
 
 #include <android/log.h>
+#include <sys/cdefs.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
 
 /*
  * Normally we strip the effects of ALOGV (VERBOSE messages),
@@ -355,11 +354,11 @@
 
 #if LOG_NDEBUG /* Production */
 #define android_testLog(prio, tag)                                           \
-  (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+  (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
                                  ANDROID_LOG_DEBUG) != 0)
 #else
 #define android_testLog(prio, tag)                                           \
-  (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+  (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
                                  ANDROID_LOG_VERBOSE) != 0)
 #endif
 
@@ -385,8 +384,6 @@
 #pragma clang diagnostic pop
 #endif
 
-#ifdef __cplusplus
-}
-#endif
+__END_DECLS
 
 #endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index b8ca475..07e8c8a 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -10,6 +10,8 @@
 #ifndef _LIBS_LOG_SAFETYNET_H
 #define _LIBS_LOG_SAFETYNET_H
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 3764faf..309f5d1 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -28,6 +28,10 @@
 #ifndef __struct_log_time_defined
 #define __struct_log_time_defined
 
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
 #ifdef __cplusplus
 
 /*
@@ -167,15 +171,15 @@
 #endif
 } __attribute__((__packed__));
 
-#else
+#else /* __cplusplus */
 
 typedef struct log_time {
   uint32_t tv_sec;
   uint32_t tv_nsec;
 } __attribute__((__packed__)) log_time;
 
-#endif
+#endif /* __cplusplus */
 
-#endif
+#endif /* __struct_log_time_defined */
 
 #endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index 01623df..a79beec 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -9,6 +9,7 @@
 #include <log/log_radio.h>
 #include <log/log_read.h>
 #include <log/log_safetynet.h>
+#include <log/log_system.h>
 #include <log/log_time.h>
 
 /*
diff --git a/liblog/include_vndk/log/log_system.h b/liblog/include_vndk/log/log_system.h
new file mode 120000
index 0000000..d0d3904
--- /dev/null
+++ b/liblog/include_vndk/log/log_system.h
@@ -0,0 +1 @@
+../../include/log/log_system.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
deleted file mode 120000
index abfe439..0000000
--- a/liblog/include_vndk/log/log_time.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/log/log_time.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 100644
index 0000000..5a09959
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#ifndef NS_PER_SEC
+#define NS_PER_SEC 1000000000ULL
+#endif
+#ifndef US_PER_SEC
+#define US_PER_SEC 1000000ULL
+#endif
+#ifndef MS_PER_SEC
+#define MS_PER_SEC 1000ULL
+#endif
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+typedef struct log_time {
+  uint32_t tv_sec;
+  uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
index 522867d..563cb3f 100644
--- a/liblog/local_logger.c
+++ b/liblog/local_logger.c
@@ -222,6 +222,7 @@
       log->last[logId] = node->prev;
     }
     list_remove(node);
+    LOG_ALWAYS_FATAL_IF(node == log->last[logId], "corrupted list");
     free(e);
   }
   /* add entry to list */
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 600f4bb..603ba24 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -590,20 +590,30 @@
 
   memset(log_msg, 0, sizeof(*log_msg));
 
+  unsigned int new_alarm = 0;
   if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+    if ((logger_list->mode & ANDROID_LOG_WRAP) &&
+        (logger_list->start.tv_sec || logger_list->start.tv_nsec)) {
+      /* b/64143705 */
+      new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10;
+      logger_list->mode &= ~ANDROID_LOG_WRAP;
+    } else {
+      new_alarm = 30;
+    }
+
     memset(&ignore, 0, sizeof(ignore));
     ignore.sa_handler = caught_signal;
     sigemptyset(&ignore.sa_mask);
     /* particularily useful if tombstone is reporting for logd */
     sigaction(SIGALRM, &ignore, &old_sigaction);
-    old_alarm = alarm(30);
+    old_alarm = alarm(new_alarm);
   }
 
   /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
   ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
   e = errno;
 
-  if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+  if (new_alarm) {
     if ((ret == 0) || (e == EINTR)) {
       e = EAGAIN;
       ret = -1;
diff --git a/liblog/logprint.c b/liblog/logprint.c
index b62f8b4..a2839bf 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -250,6 +250,7 @@
   while (!list_empty(&convertHead)) {
     struct listnode* node = list_head(&convertHead);
     list_remove(node);
+    LOG_ALWAYS_FATAL_IF(node == list_head(&convertHead), "corrupted list");
     free(node);
   }
 }
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index ab96429..275a2d6 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -64,7 +64,8 @@
     log_radio_test.cpp \
     log_read_test.cpp \
     log_system_test.cpp \
-    log_time_test.cpp
+    log_time_test.cpp \
+    log_wrap_test.cpp
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 46ec5ef..e2d5aeb 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -105,7 +105,7 @@
 }
 
 #if (defined(__ANDROID__) && defined(USING_LOGGER_DEFAULT))
-static std::string popenToString(std::string command) {
+static std::string popenToString(const std::string& command) {
   std::string ret;
 
   FILE* fp = popen(command.c_str(), "r");
@@ -129,17 +129,17 @@
 static bool isLogdwActive() {
   std::string logdwSignature =
       popenToString("grep /dev/socket/logdw /proc/net/unix");
-  size_t beginning = logdwSignature.find(" ");
+  size_t beginning = logdwSignature.find(' ');
   if (beginning == std::string::npos) return true;
-  beginning = logdwSignature.find(" ", beginning + 1);
+  beginning = logdwSignature.find(' ', beginning + 1);
   if (beginning == std::string::npos) return true;
-  size_t end = logdwSignature.find(" ", beginning + 1);
+  size_t end = logdwSignature.find(' ', beginning + 1);
   if (end == std::string::npos) return true;
-  end = logdwSignature.find(" ", end + 1);
+  end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
-  end = logdwSignature.find(" ", end + 1);
+  end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
-  end = logdwSignature.find(" ", end + 1);
+  end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
   std::string allLogdwEndpoints = popenToString(
       "grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
@@ -159,7 +159,7 @@
 
   // NB: fgrep with multiple strings is broken in Android
   for (beginning = 0;
-       (end = allLogdwEndpoints.find("\n", beginning)) != std::string::npos;
+       (end = allLogdwEndpoints.find('\n', beginning)) != std::string::npos;
        beginning = end + 1) {
     if (myPidFds.find(allLogdwEndpoints.substr(beginning, end - beginning)) !=
         std::string::npos)
@@ -3170,7 +3170,7 @@
   return (offset != std::string::npos) &&
          ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
           std::string::npos) &&
-         (content.find_first_not_of("0", offset) != offset);
+         (content.find_first_not_of('0', offset) != offset);
 }
 
 // must not be: '<needle:> 0 kB'
@@ -3239,7 +3239,7 @@
   filename = android::base::StringPrintf("/proc/%d/comm", pid);
   android::base::ReadFileToString(filename, &content);
   content = android::base::StringPrintf(
-      "%d:%s", pid, content.substr(0, content.find("\n")).c_str());
+      "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
 
   EXPECT_TRUE(IsOk(shared_ok, content));
   EXPECT_TRUE(IsOk(private_ok, content));
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
index c56fa8b..9fb5a2c 100644
--- a/liblog/tests/log_id_test.cpp
+++ b/liblog/tests/log_id_test.cpp
@@ -89,12 +89,12 @@
     ASSERT_EQ(0, pthread_create(&t[i], NULL, ConcurrentPrintFn,
                                 reinterpret_cast<void*>(i)));
   }
-  int ret = 0;
+  int ret = 1;
   for (i = 0; i < NUM_CONCURRENT; i++) {
     void* result;
     ASSERT_EQ(0, pthread_join(t[i], &result));
     int this_result = reinterpret_cast<uintptr_t>(result);
-    if ((0 == ret) && (0 != this_result)) {
+    if ((0 < ret) && (ret != this_result)) {
       ret = this_result;
     }
   }
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
new file mode 100644
index 0000000..ebf0b15
--- /dev/null
+++ b/liblog/tests/log_wrap_test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013-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 <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h>  // minimal logging API
+#include <gtest/gtest.h>
+#include <log/log_properties.h>
+#include <log/log_read.h>
+#include <log/log_time.h>
+#include <log/log_transport.h>
+
+#ifdef __ANDROID__
+static void read_with_wrap() {
+  android_set_log_transport(LOGGER_LOGD);
+
+  // Read the last line in the log to get a starting timestamp. We're assuming
+  // the log is not empty.
+  const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+  struct logger_list* logger_list =
+      android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0);
+
+  ASSERT_NE(logger_list, nullptr);
+
+  log_msg log_msg;
+  int ret = android_logger_list_read(logger_list, &log_msg);
+  android_logger_list_close(logger_list);
+  ASSERT_GT(ret, 0);
+
+  log_time start(log_msg.entry.sec, log_msg.entry.nsec);
+  ASSERT_NE(start, log_time());
+
+  logger_list =
+      android_logger_list_alloc_time(mode | ANDROID_LOG_WRAP, start, 0);
+  ASSERT_NE(logger_list, nullptr);
+
+  struct logger* logger = android_logger_open(logger_list, LOG_ID_MAIN);
+  EXPECT_NE(logger, nullptr);
+  if (logger) {
+    android_logger_list_read(logger_list, &log_msg);
+  }
+
+  android_logger_list_close(logger_list);
+}
+
+static void caught_signal(int /* signum */) {
+}
+#endif
+
+// b/64143705 confirm fixed
+TEST(liblog, wrap_mode_blocks) {
+#ifdef __ANDROID__
+
+  android::base::Timer timer;
+
+  // The read call is expected to take up to 2 hours in the happy case.
+  // We only want to make sure it waits for longer than 30s, but we can't
+  // use an alarm as the implementation uses it. So we run the test in
+  // a separate process.
+  pid_t pid = fork();
+
+  if (pid == 0) {
+    // child
+    read_with_wrap();
+    _exit(0);
+  }
+
+  struct sigaction ignore, old_sigaction;
+  memset(&ignore, 0, sizeof(ignore));
+  ignore.sa_handler = caught_signal;
+  sigemptyset(&ignore.sa_mask);
+  sigaction(SIGALRM, &ignore, &old_sigaction);
+  alarm(45);
+
+  bool killed = false;
+  for (;;) {
+    siginfo_t info = {};
+    // This wait will succeed if the child exits, or fail with EINTR if the
+    // alarm goes off first - a loose approximation to a timed wait.
+    int ret = waitid(P_PID, pid, &info, WEXITED);
+    if (ret >= 0 || errno != EINTR) {
+      EXPECT_EQ(ret, 0);
+      if (!killed) {
+        EXPECT_EQ(info.si_status, 0);
+      }
+      break;
+    }
+    unsigned int alarm_left = alarm(0);
+    if (alarm_left > 0) {
+      alarm(alarm_left);
+    } else {
+      kill(pid, SIGTERM);
+      killed = true;
+    }
+  }
+
+  alarm(0);
+  EXPECT_GT(timer.duration(), std::chrono::seconds(40));
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 68c580a..0955633 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -2,6 +2,10 @@
 
 cc_library_shared {
     name: "libmemtrack",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     srcs: ["memtrack.cpp"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index 6fe67a4..213be17 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -33,9 +33,11 @@
 
 #include "android-base/macros.h"
 
-#include "anon_vma_naming.h"
 #include "Allocator.h"
 #include "LinkedList.h"
+#include "anon_vma_naming.h"
+
+namespace android {
 
 // runtime interfaces used:
 // abort
@@ -57,10 +59,9 @@
 static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
 static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
 static constexpr size_t kMinBucketAllocationSize = 8;
-static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
-    - const_log2(kMinBucketAllocationSize) + 1;
-static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
-    / kPageSize;
+static constexpr unsigned int kNumBuckets =
+    const_log2(kMaxBucketAllocationSize) - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize / kPageSize;
 
 std::atomic<int> heap_count;
 
@@ -93,7 +94,7 @@
   void FreeLocked(void* ptr);
 
   struct MapAllocation {
-    void *ptr;
+    void* ptr;
     size_t size;
     MapAllocation* next;
   };
@@ -107,8 +108,7 @@
 }
 
 static inline unsigned int size_to_bucket(size_t size) {
-  if (size < kMinBucketAllocationSize)
-    return kMinBucketAllocationSize;
+  if (size < kMinBucketAllocationSize) return kMinBucketAllocationSize;
   return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
 }
 
@@ -140,8 +140,7 @@
 
   // Trim beginning
   if (aligned_ptr != ptr) {
-    ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
-        - reinterpret_cast<uintptr_t>(ptr);
+    ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr) - reinterpret_cast<uintptr_t>(ptr);
     munmap(ptr, extra);
     map_size -= extra;
     ptr = aligned_ptr;
@@ -151,14 +150,13 @@
   if (map_size != size) {
     assert(map_size > size);
     assert(ptr != NULL);
-    munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
-        map_size - size);
+    munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size);
   }
 
-#define PR_SET_VMA   0x53564d41
-#define PR_SET_VMA_ANON_NAME    0
-  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
-      reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size,
+        "leak_detector_malloc");
 
   return ptr;
 }
@@ -170,36 +168,31 @@
   Chunk(HeapImpl* heap, int bucket);
   ~Chunk() {}
 
-  void *Alloc();
+  void* Alloc();
   void Free(void* ptr);
   void Purge();
   bool Empty();
 
   static Chunk* ptr_to_chunk(void* ptr) {
-    return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
-        & ~(kChunkSize - 1));
+    return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr) & ~(kChunkSize - 1));
   }
   static bool is_chunk(void* ptr) {
     return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
   }
 
-  unsigned int free_count() {
-    return free_count_;
-  }
-  HeapImpl* heap() {
-    return heap_;
-  }
-  LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+  unsigned int free_count() { return free_count_; }
+  HeapImpl* heap() { return heap_; }
+  LinkedList<Chunk*> node_;  // linked list sorted by minimum free count
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Chunk);
   HeapImpl* heap_;
   unsigned int bucket_;
-  unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
-  unsigned int max_allocations_; // maximum number of allocations in the chunk
-  unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
-  unsigned int free_count_; // number of available allocations
-  unsigned int frees_since_purge_; // number of calls to Free since last Purge
+  unsigned int allocation_size_;    // size of allocations in chunk, min 8 bytes
+  unsigned int max_allocations_;    // maximum number of allocations in the chunk
+  unsigned int first_free_bitmap_;  // index into bitmap for first non-full entry
+  unsigned int free_count_;         // number of available allocations
+  unsigned int frees_since_purge_;  // number of calls to Free since last Purge
 
   // bitmap of pages that have been dirtied
   uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
@@ -210,13 +203,10 @@
   char data_[0];
 
   unsigned int ptr_to_n(void* ptr) {
-    ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
-        - reinterpret_cast<uintptr_t>(data_);
+    ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(data_);
     return offset / allocation_size_;
   }
-  void* n_to_ptr(unsigned int n) {
-    return data_ + n * allocation_size_;
-  }
+  void* n_to_ptr(unsigned int n) { return data_ + n * allocation_size_; }
 };
 static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
 
@@ -225,23 +215,27 @@
   assert(count == sizeof(Chunk));
   void* mem = MapAligned(kChunkSize, kChunkSize);
   if (!mem) {
-    abort(); //throw std::bad_alloc;
+    abort();  // throw std::bad_alloc;
   }
 
   return mem;
 }
 
 // Override new operator on chunk to use mmap to allocate kChunkSize
-void Chunk::operator delete(void *ptr) {
+void Chunk::operator delete(void* ptr) {
   assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
   munmap(ptr, kChunkSize);
 }
 
-Chunk::Chunk(HeapImpl* heap, int bucket) :
-    node_(this), heap_(heap), bucket_(bucket), allocation_size_(
-        bucket_to_size(bucket)), max_allocations_(
-        kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
-        max_allocations_), frees_since_purge_(0) {
+Chunk::Chunk(HeapImpl* heap, int bucket)
+    : node_(this),
+      heap_(heap),
+      bucket_(bucket),
+      allocation_size_(bucket_to_size(bucket)),
+      max_allocations_(kUsableChunkSize / allocation_size_),
+      first_free_bitmap_(0),
+      free_count_(max_allocations_),
+      frees_since_purge_(0) {
   memset(dirty_pages_, 0, sizeof(dirty_pages_));
   memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
 }
@@ -254,8 +248,7 @@
   assert(free_count_ > 0);
 
   unsigned int i = first_free_bitmap_;
-  while (free_bitmap_[i] == 0)
-    i++;
+  while (free_bitmap_[i] == 0) i++;
   assert(i < arraysize(free_bitmap_));
   unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
   assert(free_bitmap_[i] & (1U << bit));
@@ -306,38 +299,35 @@
 void Chunk::Purge() {
   frees_since_purge_ = 0;
 
-  //unsigned int allocsPerPage = kPageSize / allocation_size_;
+  // unsigned int allocsPerPage = kPageSize / allocation_size_;
 }
 
 // Override new operator on HeapImpl to use mmap to allocate a page
-void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
-    noexcept {
+void* HeapImpl::operator new(std::size_t count __attribute__((unused))) noexcept {
   assert(count == sizeof(HeapImpl));
   void* mem = MapAligned(kPageSize, kPageSize);
   if (!mem) {
-    abort(); //throw std::bad_alloc;
+    abort();  // throw std::bad_alloc;
   }
 
   heap_count++;
   return mem;
 }
 
-void HeapImpl::operator delete(void *ptr) {
+void HeapImpl::operator delete(void* ptr) {
   munmap(ptr, kPageSize);
 }
 
-HeapImpl::HeapImpl() :
-    free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
-}
+HeapImpl::HeapImpl() : free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {}
 
 bool HeapImpl::Empty() {
   for (unsigned int i = 0; i < kNumBuckets; i++) {
-    for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+    for (LinkedList<Chunk*>* it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
       if (!it->data()->Empty()) {
         return false;
       }
     }
-    for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+    for (LinkedList<Chunk*>* it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
       if (!it->data()->Empty()) {
         return false;
       }
@@ -350,12 +340,12 @@
 HeapImpl::~HeapImpl() {
   for (unsigned int i = 0; i < kNumBuckets; i++) {
     while (!free_chunks_[i].empty()) {
-      Chunk *chunk = free_chunks_[i].next()->data();
+      Chunk* chunk = free_chunks_[i].next()->data();
       chunk->node_.remove();
       delete chunk;
     }
     while (!full_chunks_[i].empty()) {
-      Chunk *chunk = full_chunks_[i].next()->data();
+      Chunk* chunk = full_chunks_[i].next()->data();
       chunk->node_.remove();
       delete chunk;
     }
@@ -373,18 +363,18 @@
   }
   int bucket = size_to_bucket(size);
   if (free_chunks_[bucket].empty()) {
-    Chunk *chunk = new Chunk(this, bucket);
+    Chunk* chunk = new Chunk(this, bucket);
     free_chunks_[bucket].insert(chunk->node_);
   }
   return free_chunks_[bucket].next()->data()->Alloc();
 }
 
-void HeapImpl::Free(void *ptr) {
+void HeapImpl::Free(void* ptr) {
   std::lock_guard<std::mutex> lk(m_);
   FreeLocked(ptr);
 }
 
-void HeapImpl::FreeLocked(void *ptr) {
+void HeapImpl::FreeLocked(void* ptr) {
   if (!Chunk::is_chunk(ptr)) {
     HeapImpl::MapFree(ptr);
   } else {
@@ -397,12 +387,11 @@
 void* HeapImpl::MapAlloc(size_t size) {
   size = (size + kPageSize - 1) & ~(kPageSize - 1);
 
-  MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
-      sizeof(MapAllocation)));
+  MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(sizeof(MapAllocation)));
   void* ptr = MapAligned(size, kChunkSize);
   if (!ptr) {
     FreeLocked(allocation);
-    abort(); //throw std::bad_alloc;
+    abort();  // throw std::bad_alloc;
   }
   allocation->ptr = ptr;
   allocation->size = size;
@@ -412,10 +401,9 @@
   return ptr;
 }
 
-void HeapImpl::MapFree(void *ptr) {
-  MapAllocation **allocation = &map_allocation_list_;
-  while (*allocation && (*allocation)->ptr != ptr)
-    allocation = &(*allocation)->next;
+void HeapImpl::MapFree(void* ptr) {
+  MapAllocation** allocation = &map_allocation_list_;
+  while (*allocation && (*allocation)->ptr != ptr) allocation = &(*allocation)->next;
 
   assert(*allocation != nullptr);
 
@@ -425,22 +413,22 @@
   *allocation = (*allocation)->next;
 }
 
-void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFreeList(Chunk* chunk, int bucket) {
   MoveToList(chunk, &free_chunks_[bucket]);
 }
 
-void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+void HeapImpl::MoveToFullList(Chunk* chunk, int bucket) {
   MoveToList(chunk, &full_chunks_[bucket]);
 }
 
-void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+void HeapImpl::MoveToList(Chunk* chunk, LinkedList<Chunk*>* head) {
   // Remove from old list
   chunk->node_.remove();
 
-  LinkedList<Chunk*> *node = head;
+  LinkedList<Chunk*>* node = head;
   // Insert into new list, sorted by lowest free count
-  while (node->next() != head && node->data() != nullptr
-      && node->data()->free_count() < chunk->free_count())
+  while (node->next() != head && node->data() != nullptr &&
+         node->data()->free_count() < chunk->free_count())
     node = node->next();
 
   node->insert(chunk->node_);
@@ -469,10 +457,12 @@
   impl_->Free(ptr);
 }
 
-void Heap::deallocate(HeapImpl*impl, void* ptr) {
+void Heap::deallocate(HeapImpl* impl, void* ptr) {
   impl->Free(ptr);
 }
 
 bool Heap::empty() {
   return impl_->Empty();
 }
+
+}  // namespace android
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
index 5390739..837a12b 100644
--- a/libmemunreachable/Allocator.h
+++ b/libmemunreachable/Allocator.h
@@ -27,18 +27,20 @@
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
+
+namespace android {
+
 extern std::atomic<int> heap_count;
 
 class HeapImpl;
 
-template<typename T>
+template <typename T>
 class Allocator;
 
-
 // Non-templated class that implements wraps HeapImpl to keep
 // implementation out of the header file
 class Heap {
-public:
+ public:
   Heap();
   ~Heap();
 
@@ -59,110 +61,99 @@
   static void deallocate(HeapImpl* impl, void* ptr);
 
   // Allocate a class of type T
-  template<class T>
+  template <class T>
   T* allocate() {
     return reinterpret_cast<T*>(allocate(sizeof(T)));
   }
 
   // Comparators, copied objects will be equal
-  bool operator ==(const Heap& other) const {
-    return impl_ == other.impl_;
-  }
-  bool operator !=(const Heap& other) const {
-    return !(*this == other);
-  }
+  bool operator==(const Heap& other) const { return impl_ == other.impl_; }
+  bool operator!=(const Heap& other) const { return !(*this == other); }
 
   // std::unique_ptr wrapper that allocates using allocate and deletes using
   // deallocate
-  template<class T>
+  template <class T>
   using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
 
-  template<class T, class... Args>
+  template <class T, class... Args>
   unique_ptr<T> make_unique(Args&&... args) {
     HeapImpl* impl = impl_;
-    return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
-        [impl](void* ptr) {
-          reinterpret_cast<T*>(ptr)->~T();
-          deallocate(impl, ptr);
-        });
+    return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...), [impl](void* ptr) {
+      reinterpret_cast<T*>(ptr)->~T();
+      deallocate(impl, ptr);
+    });
   }
 
   // std::unique_ptr wrapper that allocates using allocate and deletes using
   // deallocate
-  template<class T>
+  template <class T>
   using shared_ptr = std::shared_ptr<T>;
 
-  template<class T, class... Args>
+  template <class T, class... Args>
   shared_ptr<T> make_shared(Args&&... args);
 
-protected:
+ protected:
   HeapImpl* impl_;
   bool owns_impl_;
 };
 
 // STLAllocator implements the std allocator interface on top of a Heap
-template<typename T>
+template <typename T>
 class STLAllocator {
-public:
+ public:
   using value_type = T;
-  ~STLAllocator() {
-  }
+  ~STLAllocator() {}
 
   // Construct an STLAllocator on top of a Heap
-  STLAllocator(const Heap& heap) :  // NOLINT, implicit
-      heap_(heap) {
-  }
+  STLAllocator(const Heap& heap)
+      :  // NOLINT, implicit
+        heap_(heap) {}
 
   // Rebind an STLAllocator from an another STLAllocator
-  template<typename U>
-  STLAllocator(const STLAllocator<U>& other) :  // NOLINT, implicit
-      heap_(other.heap_) {
-  }
+  template <typename U>
+  STLAllocator(const STLAllocator<U>& other)
+      :  // NOLINT, implicit
+        heap_(other.heap_) {}
 
   STLAllocator(const STLAllocator&) = default;
   STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
 
-  T* allocate(std::size_t n) {
-    return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
-  }
+  T* allocate(std::size_t n) { return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T))); }
 
-  void deallocate(T* ptr, std::size_t) {
-    heap_.deallocate(ptr);
-  }
+  void deallocate(T* ptr, std::size_t) { heap_.deallocate(ptr); }
 
-  template<typename U>
-  bool operator ==(const STLAllocator<U>& other) const {
+  template <typename U>
+  bool operator==(const STLAllocator<U>& other) const {
     return heap_ == other.heap_;
   }
-  template<typename U>
-  inline bool operator !=(const STLAllocator<U>& other) const {
+  template <typename U>
+  inline bool operator!=(const STLAllocator<U>& other) const {
     return !(this == other);
   }
 
-  template<typename U>
+  template <typename U>
   friend class STLAllocator;
 
-protected:
+ protected:
   Heap heap_;
 };
 
-
 // Allocator extends STLAllocator with some convenience methods for allocating
 // a single object and for constructing unique_ptr and shared_ptr objects with
 // appropriate deleters.
-template<class T>
+template <class T>
 class Allocator : public STLAllocator<T> {
  public:
   ~Allocator() {}
 
-  Allocator(const Heap& other) : // NOLINT, implicit
-      STLAllocator<T>(other) {
-  }
+  Allocator(const Heap& other)
+      :  // NOLINT, implicit
+        STLAllocator<T>(other) {}
 
-  template<typename U>
-  Allocator(const STLAllocator<U>& other) :  // NOLINT, implicit
-      STLAllocator<T>(other) {
-  }
+  template <typename U>
+  Allocator(const STLAllocator<U>& other)
+      :  // NOLINT, implicit
+        STLAllocator<T>(other) {}
 
   Allocator(const Allocator&) = default;
   Allocator<T>& operator=(const Allocator<T>&) = default;
@@ -171,24 +162,20 @@
   using STLAllocator<T>::deallocate;
   using STLAllocator<T>::heap_;
 
-  T* allocate() {
-    return STLAllocator<T>::allocate(1);
-  }
-  void deallocate(void* ptr) {
-    heap_.deallocate(ptr);
-  }
+  T* allocate() { return STLAllocator<T>::allocate(1); }
+  void deallocate(void* ptr) { heap_.deallocate(ptr); }
 
   using shared_ptr = Heap::shared_ptr<T>;
 
-  template<class... Args>
-  shared_ptr make_shared(Args&& ...args) {
+  template <class... Args>
+  shared_ptr make_shared(Args&&... args) {
     return heap_.template make_shared<T>(std::forward<Args>(args)...);
   }
 
   using unique_ptr = Heap::unique_ptr<T>;
 
-  template<class... Args>
-  unique_ptr make_unique(Args&& ...args) {
+  template <class... Args>
+  unique_ptr make_unique(Args&&... args) {
     return heap_.template make_unique<T>(std::forward<Args>(args)...);
   }
 };
@@ -196,33 +183,36 @@
 // std::unique_ptr wrapper that allocates using allocate and deletes using
 // deallocate.  Implemented outside class definition in order to pass
 // Allocator<T> to shared_ptr.
-template<class T, class... Args>
+template <class T, class... Args>
 inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
   return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
-      std::forward<Args>(args)...);
+                                                        std::forward<Args>(args)...);
 }
 
 namespace allocator {
 
-template<class T>
+template <class T>
 using vector = std::vector<T, Allocator<T>>;
 
-template<class T>
+template <class T>
 using list = std::list<T, Allocator<T>>;
 
-template<class Key, class T, class Compare = std::less<Key>>
+template <class Key, class T, class Compare = std::less<Key>>
 using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
 
-template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
-using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+template <class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map =
+    std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
 
-template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
 using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
 
-template<class Key, class Compare = std::less<Key>>
+template <class Key, class Compare = std::less<Key>>
 using set = std::set<Key, Compare, Allocator<Key>>;
 
 using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
 }
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index cdac76b..8b76a65 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -6,7 +6,6 @@
         "-Wextra",
         "-Werror",
     ],
-    clang: true,
     shared_libs: [
         "libbase",
     ],
@@ -26,6 +25,7 @@
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "Allocator.cpp",
+        "Binder.cpp",
         "HeapWalker.cpp",
         "LeakFolding.cpp",
         "LeakPipe.cpp",
@@ -84,3 +84,18 @@
         },
     },
 }
+
+cc_test {
+    name: "memunreachable_binder_test",
+    defaults: ["libmemunreachable_defaults"],
+    srcs: [
+        "tests/Binder_test.cpp",
+        "tests/MemUnreachable_test.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libhwbinder",
+        "libmemunreachable",
+        "libutils",
+    ],
+}
diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp
new file mode 100644
index 0000000..60512a3
--- /dev/null
+++ b/libmemunreachable/Binder.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 <sys/cdefs.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include "Binder.h"
+#include "log.h"
+
+__BEGIN_DECLS
+
+// Weak undefined references to the symbols in libbinder and libhwbinder
+// so that libmemunreachable can call them in processes that have them
+// loaded without requiring libmemunreachable to have dependencies on them.
+ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*);
+ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*);
+
+__END_DECLS
+
+namespace android {
+
+static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs,
+                                     std::function<ssize_t(size_t, uintptr_t*)> fn) {
+  if (fn == nullptr) {
+    return true;
+  }
+
+  size_t size = refs.size();
+
+  do {
+    refs.resize(size);
+
+    ssize_t ret = fn(refs.size(), refs.data());
+    if (ret < 0) {
+      return false;
+    }
+
+    size = ret;
+  } while (size > refs.size());
+
+  refs.resize(size);
+  return true;
+}
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs) {
+  refs.clear();
+
+  allocator::vector<uintptr_t> binder_refs{refs.get_allocator()};
+  if (BinderReferencesToVector(refs, getBinderKernelReferences)) {
+    refs.insert(refs.end(), binder_refs.begin(), binder_refs.end());
+  } else {
+    MEM_ALOGE("getBinderKernelReferences failed");
+  }
+
+  allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()};
+  if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) {
+    refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end());
+  } else {
+    MEM_ALOGE("getHWBinderKernelReferences failed");
+  }
+
+  return true;
+}
+
+}  // namespace android
diff --git a/libunwindstack/Log.h b/libmemunreachable/Binder.h
similarity index 65%
copy from libunwindstack/Log.h
copy to libmemunreachable/Binder.h
index 2d01aa8..bf4fd3e 100644
--- a/libunwindstack/Log.h
+++ b/libmemunreachable/Binder.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 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.
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#ifndef LIBMEMUNREACHABLE_BINDER_H_
+#define LIBMEMUNREACHABLE_BINDER_H_
 
-#include <stdint.h>
+#include "Allocator.h"
 
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+namespace android {
 
-#endif  // _LIBUNWINDSTACK_LOG_H
+bool BinderReferences(allocator::vector<uintptr_t>& refs);
+
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_BINDER_H_
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index c365ae5..2403ad0 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -28,6 +28,8 @@
 #include "ScopedSignalHandler.h"
 #include "log.h"
 
+namespace android {
+
 bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
   if (end == begin) {
     end = begin + 1;
@@ -114,8 +116,8 @@
   return true;
 }
 
-bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
-    size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit, size_t* num_leaks_out,
+                        size_t* leak_bytes_out) {
   leaked.clear();
 
   size_t num_leaks = 0;
@@ -148,9 +150,9 @@
 
 static bool MapOverPage(void* addr) {
   const size_t page_size = sysconf(_SC_PAGE_SIZE);
-  void *page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size-1));
+  void* page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size - 1));
 
-  void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
+  void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
   if (ret == MAP_FAILED) {
     MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
     return false;
@@ -159,7 +161,8 @@
   return true;
 }
 
-void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) {
+void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si,
+                                void* /*uctx*/) {
   uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
   if (addr != walking_ptr_) {
     handler.reset();
@@ -172,3 +175,5 @@
 }
 
 ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+
+}  // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index b25696f..5c7ec13 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -25,6 +25,8 @@
 #include "ScopedSignalHandler.h"
 #include "Tarjan.h"
 
+namespace android {
+
 // A range [begin, end)
 struct Range {
   uintptr_t begin;
@@ -34,31 +36,31 @@
   bool operator==(const Range& other) const {
     return this->begin == other.begin && this->end == other.end;
   }
-  bool operator!=(const Range& other) const {
-    return !(*this == other);
-  }
+  bool operator!=(const Range& other) const { return !(*this == other); }
 };
 
 // Comparator for Ranges that returns equivalence for overlapping ranges
 struct compare_range {
-  bool operator()(const Range& a, const Range& b) const {
-    return a.end <= b.begin;
-  }
+  bool operator()(const Range& a, const Range& b) const { return a.end <= b.begin; }
 };
 
 class HeapWalker {
  public:
-  explicit HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
-    allocations_(allocator), allocation_bytes_(0),
-	roots_(allocator), root_vals_(allocator),
-	segv_handler_(allocator), walking_ptr_(0) {
+  explicit HeapWalker(Allocator<HeapWalker> allocator)
+      : allocator_(allocator),
+        allocations_(allocator),
+        allocation_bytes_(0),
+        roots_(allocator),
+        root_vals_(allocator),
+        segv_handler_(allocator),
+        walking_ptr_(0) {
     valid_allocations_range_.end = 0;
     valid_allocations_range_.begin = ~valid_allocations_range_.end;
 
-    segv_handler_.install(SIGSEGV,
-        [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+    segv_handler_.install(
+        SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
           this->HandleSegFault(handler, signal, siginfo, uctx);
-      });
+        });
   }
 
   ~HeapWalker() {}
@@ -68,15 +70,14 @@
 
   bool DetectLeaks();
 
-  bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
-      size_t* leak_bytes);
+  bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks, size_t* leak_bytes);
   size_t Allocations();
   size_t AllocationBytes();
 
-  template<class F>
+  template <class F>
   void ForEachPtrInRange(const Range& range, F&& f);
 
-  template<class F>
+  template <class F>
   void ForEachAllocation(F&& f);
 
   struct AllocationInfo {
@@ -84,7 +85,6 @@
   };
 
  private:
-
   void RecurseRoot(const Range& root);
   bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
   void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*);
@@ -103,7 +103,7 @@
   uintptr_t walking_ptr_;
 };
 
-template<class F>
+template <class F>
 inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
   uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
   // TODO(ccross): we might need to consider a pointer to the end of a buffer
@@ -118,7 +118,7 @@
   }
 }
 
-template<class F>
+template <class F>
 inline void HeapWalker::ForEachAllocation(F&& f) {
   for (auto& it : allocations_) {
     const Range& range = it.first;
@@ -127,4 +127,6 @@
   }
 }
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
index eaeeea7..de64b64 100644
--- a/libmemunreachable/Leak.h
+++ b/libmemunreachable/Leak.h
@@ -26,9 +26,9 @@
 // as a key in std::unordered_map.
 namespace std {
 
-template<>
-struct hash<Leak::Backtrace> {
-  std::size_t operator()(const Leak::Backtrace& key) const {
+template <>
+struct hash<android::Leak::Backtrace> {
+  std::size_t operator()(const android::Leak::Backtrace& key) const {
     std::size_t seed = 0;
 
     hash_combine(seed, key.num_frames);
@@ -40,7 +40,7 @@
   }
 
  private:
-  template<typename T>
+  template <typename T>
   inline void hash_combine(std::size_t& seed, const T& v) const {
     std::hash<T> hasher;
     seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
@@ -49,9 +49,12 @@
 
 }  // namespace std
 
+namespace android {
+
 static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
   return (lhs.num_frames == rhs.num_frames) &&
-      memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+         memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
 }
 
 #endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index be4d20c..69f320c 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -22,6 +22,8 @@
 #include "Tarjan.h"
 #include "log.h"
 
+namespace android {
+
 // Converts possibly cyclic graph of leaks to a DAG by combining
 // strongly-connected components into a object, stored in the scc pointer
 // of each node in the component.
@@ -31,11 +33,11 @@
 
   Allocator<SCCInfo> scc_allocator = allocator_;
 
-  for (auto& scc_nodes: scc_list) {
+  for (auto& scc_nodes : scc_list) {
     Allocator<SCCInfo>::unique_ptr leak_scc;
     leak_scc = scc_allocator.make_unique(scc_allocator);
 
-    for (auto& node: scc_nodes) {
+    for (auto& node : scc_nodes) {
       node->ptr->scc = leak_scc.get();
       leak_scc->count++;
       leak_scc->size += node->ptr->range.size();
@@ -46,7 +48,7 @@
 
   for (auto& it : leak_map_) {
     LeakInfo& leak = it.second;
-    for (auto& ref: leak.node.references_out) {
+    for (auto& ref : leak.node.references_out) {
       if (leak.scc != ref->ptr->scc) {
         leak.scc->node.Edge(&ref->ptr->scc->node);
       }
@@ -55,17 +57,14 @@
 }
 
 void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
-  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
-      [&](SCCInfo* scc) {
-        if (scc->accumulator != dominator) {
-          scc->accumulator = dominator;
-          dominator->cuumulative_size += scc->size;
-          dominator->cuumulative_count += scc->count;
-          scc->node.Foreach([&](SCCInfo* ref) {
-            walk(ref);
-          });
-        }
-      });
+  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+    if (scc->accumulator != dominator) {
+      scc->accumulator = dominator;
+      dominator->cuumulative_size += scc->size;
+      dominator->cuumulative_count += scc->count;
+      scc->node.Foreach([&](SCCInfo* ref) { walk(ref); });
+    }
+  });
   walk(dominator);
 }
 
@@ -73,27 +72,25 @@
   Allocator<LeakInfo> leak_allocator = allocator_;
 
   // Find all leaked allocations insert them into leak_map_ and leak_graph_
-  heap_walker_.ForEachAllocation(
-      [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
-        if (!allocation.referenced_from_root) {
-          auto it = leak_map_.emplace(std::piecewise_construct,
-              std::forward_as_tuple(range),
-              std::forward_as_tuple(range, allocator_));
-          LeakInfo& leak = it.first->second;
-          leak_graph_.push_back(&leak.node);
-        }
-      });
+  heap_walker_.ForEachAllocation([&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+    if (!allocation.referenced_from_root) {
+      auto it = leak_map_.emplace(std::piecewise_construct, std::forward_as_tuple(range),
+                                  std::forward_as_tuple(range, allocator_));
+      LeakInfo& leak = it.first->second;
+      leak_graph_.push_back(&leak.node);
+    }
+  });
 
   // Find references between leaked allocations and connect them in leak_graph_
   for (auto& it : leak_map_) {
     LeakInfo& leak = it.second;
     heap_walker_.ForEachPtrInRange(leak.range,
-        [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
-          if (!ptr_info->referenced_from_root) {
-            LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
-            leak.node.Edge(&ptr_leak->node);
-          }
-        });
+                                   [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+                                     if (!ptr_info->referenced_from_root) {
+                                       LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+                                       leak.node.Edge(&ptr_leak->node);
+                                     }
+                                   });
   }
 
   // Convert the cyclic graph to a DAG by grouping strongly connected components
@@ -110,8 +107,8 @@
   return true;
 }
 
-bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
-    size_t* num_leaks_out, size_t* leak_bytes_out) {
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked, size_t* num_leaks_out,
+                         size_t* leak_bytes_out) {
   size_t num_leaks = 0;
   size_t leak_bytes = 0;
   for (auto& it : leak_map_) {
@@ -123,9 +120,8 @@
   for (auto& it : leak_map_) {
     const LeakInfo& leak = it.second;
     if (leak.scc->dominator) {
-      leaked.emplace_back(Leak{leak.range,
-        leak.scc->cuumulative_count - 1,
-        leak.scc->cuumulative_size - leak.range.size()});
+      leaked.emplace_back(Leak{leak.range, leak.scc->cuumulative_count - 1,
+                               leak.scc->cuumulative_size - leak.range.size()});
     }
   }
 
@@ -138,3 +134,5 @@
 
   return true;
 }
+
+}  // namespace android
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
index 9c6a525..09affac 100644
--- a/libmemunreachable/LeakFolding.h
+++ b/libmemunreachable/LeakFolding.h
@@ -19,11 +19,16 @@
 
 #include "HeapWalker.h"
 
+namespace android {
+
 class LeakFolding {
  public:
   LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
-   : allocator_(allocator), heap_walker_(heap_walker),
-     leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+      : allocator_(allocator),
+        heap_walker_(heap_walker),
+        leak_map_(allocator),
+        leak_graph_(allocator),
+        leak_scc_(allocator) {}
 
   bool FoldLeaks();
 
@@ -33,8 +38,7 @@
     size_t referenced_size;
   };
 
-  bool Leaked(allocator::vector<Leak>& leaked,
-      size_t* num_leaks_out, size_t* leak_bytes_out);
+  bool Leaked(allocator::vector<Leak>& leaked, size_t* num_leaks_out, size_t* leak_bytes_out);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LeakFolding);
@@ -54,9 +58,15 @@
     bool dominator;
     SCCInfo* accumulator;
 
-    explicit SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
-        count(0), size(0), cuumulative_count(0), cuumulative_size(0),
-        dominator(false), accumulator(nullptr) {}
+    explicit SCCInfo(Allocator<SCCInfo> allocator)
+        : node(this, allocator),
+          count(0),
+          size(0),
+          cuumulative_count(0),
+          cuumulative_size(0),
+          dominator(false),
+          accumulator(nullptr) {}
+
    private:
     SCCInfo(SCCInfo&&) = delete;
     DISALLOW_COPY_AND_ASSIGN(SCCInfo);
@@ -71,8 +81,7 @@
     SCCInfo* scc;
 
     LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
-        : node(this, allocator), range(range),
-          scc(nullptr) {}
+        : node(this, allocator), range(range), scc(nullptr) {}
 
    private:
     DISALLOW_COPY_AND_ASSIGN(LeakInfo);
@@ -86,4 +95,6 @@
   allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
 };
 
-#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 78117e2..8ea9ad6 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -21,9 +21,11 @@
 
 #include "log.h"
 
+namespace android {
+
 bool LeakPipe::SendFd(int sock, int fd) {
-  struct msghdr hdr{};
-  struct iovec iov{};
+  struct msghdr hdr {};
+  struct iovec iov {};
   unsigned int data = 0xfdfdfdfd;
   alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
 
@@ -56,8 +58,8 @@
 }
 
 int LeakPipe::ReceiveFd(int sock) {
-  struct msghdr hdr{};
-  struct iovec iov{};
+  struct msghdr hdr {};
+  struct iovec iov {};
   unsigned int data;
   alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
 
@@ -87,3 +89,5 @@
 
   return *(int*)CMSG_DATA(cmsg);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3ea2d8f..94d4aa4 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -26,6 +26,8 @@
 #include "ScopedPipe.h"
 #include "log.h"
 
+namespace android {
+
 // LeakPipe implements a pipe that can transfer vectors of simple objects
 // between processes.  The pipe is created in the sending process and
 // transferred over a socketpair that was created before forking.  This ensures
@@ -34,15 +36,13 @@
 class LeakPipe {
  public:
   LeakPipe() {
-    int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+    int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv_);
     if (ret < 0) {
       MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
     }
   }
 
-  ~LeakPipe() {
-    Close();
-  }
+  ~LeakPipe() { Close(); }
 
   void Close() {
     close(sv_[0]);
@@ -77,13 +77,9 @@
    public:
     LeakPipeBase() : fd_(-1) {}
 
-    ~LeakPipeBase() {
-      Close();
-    }
+    ~LeakPipeBase() { Close(); }
 
-    void SetFd(int fd) {
-      fd_ = fd;
-    }
+    void SetFd(int fd) { fd_ = fd; }
 
     void Close() {
       close(fd_);
@@ -101,7 +97,7 @@
    public:
     using LeakPipeBase::LeakPipeBase;
 
-    template<typename T>
+    template <typename T>
     bool Send(const T& value) {
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
       if (ret < 0) {
@@ -115,7 +111,7 @@
       return true;
     }
 
-    template<class T, class Alloc = std::allocator<T>>
+    template <class T, class Alloc = std::allocator<T>>
     bool SendVector(const std::vector<T, Alloc>& vector) {
       size_t size = vector.size() * sizeof(T);
       if (!Send(size)) {
@@ -139,7 +135,7 @@
    public:
     using LeakPipeBase::LeakPipeBase;
 
-    template<typename T>
+    template <typename T>
     bool Receive(T* value) {
       ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
       if (ret < 0) {
@@ -153,7 +149,7 @@
       return true;
     }
 
-    template<class T, class Alloc = std::allocator<T>>
+    template <class T, class Alloc = std::allocator<T>>
     bool ReceiveVector(std::vector<T, Alloc>& vector) {
       size_t size = 0;
       if (!Receive(&size)) {
@@ -178,16 +174,11 @@
 
       return true;
     }
-
   };
 
-  LeakPipeReceiver& Receiver() {
-    return receiver_;
-  }
+  LeakPipeReceiver& Receiver() { return receiver_; }
 
-  LeakPipeSender& Sender() {
-    return sender_;
-  }
+  LeakPipeSender& Sender() { return sender_; }
 
  private:
   LeakPipeReceiver receiver_;
@@ -198,4 +189,6 @@
   int sv_[2];
 };
 
-#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
index d3580c0..4ea0542 100644
--- a/libmemunreachable/LineBuffer.cpp
+++ b/libmemunreachable/LineBuffer.cpp
@@ -23,8 +23,10 @@
 
 #include "LineBuffer.h"
 
-LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
-}
+namespace android {
+
+LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len)
+    : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {}
 
 bool LineBuffer::GetLine(char** line, size_t* line_len) {
   while (true) {
@@ -60,3 +62,5 @@
     bytes_ += bytes;
   }
 }
+
+}  // namespace android
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
index a015c46..cc6cd0c 100644
--- a/libmemunreachable/LineBuffer.h
+++ b/libmemunreachable/LineBuffer.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace android {
+
 class LineBuffer {
  public:
   LineBuffer(int fd, char* buffer, size_t buffer_len);
@@ -33,4 +35,6 @@
   size_t bytes_ = 0;
 };
 
-#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
+}  // namespace android
+
+#endif  // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
index 132842d..36fe9fd 100644
--- a/libmemunreachable/LinkedList.h
+++ b/libmemunreachable/LinkedList.h
@@ -17,44 +17,47 @@
 #ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_
 #define LIBMEMUNREACHABLE_LINKED_LIST_H_
 
-template<class T>
+namespace android {
+
+template <class T>
 class LinkedList {
-public:
-    LinkedList() : next_(this), prev_(this), data_() {}
-    explicit LinkedList(T data) : LinkedList() {
-        data_ = data;
-    }
-    ~LinkedList() {}
-    void insert(LinkedList<T>& node) {
-        assert(node.empty());
-        node.next_ = this->next_;
-        node.next_->prev_ = &node;
-        this->next_ = &node;
-        node.prev_ = this;
-    }
-    void remove() {
-        this->next_->prev_ = this->prev_;
-        this->prev_->next_ = this->next_;
-        this->next_ = this;
-        this->prev_ = this;
-    }
-    T data() { return data_; }
-    bool empty() { return next_ == this && prev_ == this; }
-    LinkedList<T> *next() { return next_; }
-private:
-    LinkedList<T> *next_;
-    LinkedList<T> *prev_;
-    T data_;
+ public:
+  LinkedList() : next_(this), prev_(this), data_() {}
+  explicit LinkedList(T data) : LinkedList() { data_ = data; }
+  ~LinkedList() {}
+  void insert(LinkedList<T>& node) {
+    assert(node.empty());
+    node.next_ = this->next_;
+    node.next_->prev_ = &node;
+    this->next_ = &node;
+    node.prev_ = this;
+  }
+  void remove() {
+    this->next_->prev_ = this->prev_;
+    this->prev_->next_ = this->next_;
+    this->next_ = this;
+    this->prev_ = this;
+  }
+  T data() { return data_; }
+  bool empty() { return next_ == this && prev_ == this; }
+  LinkedList<T>* next() { return next_; }
+
+ private:
+  LinkedList<T>* next_;
+  LinkedList<T>* prev_;
+  T data_;
 };
 
-template<class T>
+template <class T>
 class LinkedListHead {
-public:
-    LinkedListHead() : node_() {}
-    ~LinkedListHead() {}
+ public:
+  LinkedListHead() : node_() {}
+  ~LinkedListHead() {}
 
-private:
-    LinkedList<T> node_;
+ private:
+  LinkedList<T> node_;
 };
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 1c84744..24fdc7f 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -15,18 +15,20 @@
  */
 
 #include <inttypes.h>
+#include <string.h>
 
 #include <functional>
 #include <iomanip>
 #include <mutex>
-#include <string>
 #include <sstream>
+#include <string>
 #include <unordered_map>
 
-#include <backtrace.h>
 #include <android-base/macros.h>
+#include <backtrace.h>
 
 #include "Allocator.h"
+#include "Binder.h"
 #include "HeapWalker.h"
 #include "Leak.h"
 #include "LeakFolding.h"
@@ -37,30 +39,34 @@
 #include "Semaphore.h"
 #include "ThreadCapture.h"
 
-#include "memunreachable/memunreachable.h"
 #include "bionic.h"
 #include "log.h"
-
-const size_t Leak::contents_length;
+#include "memunreachable/memunreachable.h"
 
 using namespace std::chrono_literals;
 
+namespace android {
+
+const size_t Leak::contents_length;
+
 class MemUnreachable {
  public:
-  MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
-      heap_walker_(allocator_) {}
+  MemUnreachable(pid_t pid, Allocator<void> allocator)
+      : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {}
   bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
-      const allocator::vector<Mapping>& mappings);
-  bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
-      size_t* num_leaks, size_t* leak_bytes);
+                          const allocator::vector<Mapping>& mappings,
+                          const allocator::vector<uintptr_t>& refs);
+  bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks,
+                            size_t* leak_bytes);
   size_t Allocations() { return heap_walker_.Allocations(); }
   size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+
  private:
   bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
-      allocator::vector<Mapping>& heap_mappings,
-      allocator::vector<Mapping>& anon_mappings,
-      allocator::vector<Mapping>& globals_mappings,
-      allocator::vector<Mapping>& stack_mappings);
+                        allocator::vector<Mapping>& heap_mappings,
+                        allocator::vector<Mapping>& anon_mappings,
+                        allocator::vector<Mapping>& globals_mappings,
+                        allocator::vector<Mapping>& stack_mappings);
   DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
   pid_t pid_;
   Allocator<void> allocator_;
@@ -68,16 +74,18 @@
 };
 
 static void HeapIterate(const Mapping& heap_mapping,
-    const std::function<void(uintptr_t, size_t)>& func) {
+                        const std::function<void(uintptr_t, size_t)>& func) {
   malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
-      [](uintptr_t base, size_t size, void* arg) {
-    auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
-    (*f)(base, size);
-  }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+                 [](uintptr_t base, size_t size, void* arg) {
+                   auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+                   (*f)(base, size);
+                 },
+                 const_cast<void*>(reinterpret_cast<const void*>(&func)));
 }
 
 bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
-    const allocator::vector<Mapping>& mappings) {
+                                        const allocator::vector<Mapping>& mappings,
+                                        const allocator::vector<uintptr_t>& refs) {
   MEM_ALOGI("searching process %d for allocations", pid_);
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
@@ -113,13 +121,15 @@
     heap_walker_.Root(thread_it->regs);
   }
 
+  heap_walker_.Root(refs);
+
   MEM_ALOGI("searching done");
 
   return true;
 }
 
-bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
-    size_t limit, size_t* num_leaks, size_t* leak_bytes) {
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+                                          size_t* num_leaks, size_t* leak_bytes) {
   MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
   leaks.clear();
 
@@ -127,7 +137,6 @@
     return false;
   }
 
-
   allocator::vector<Range> leaked1{allocator_};
   heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
 
@@ -152,12 +161,12 @@
   // in backtrace_map.
   leaks.reserve(leaked.size());
 
-  for (auto& it: leaked) {
+  for (auto& it : leaked) {
     leaks.emplace_back();
     Leak* leak = &leaks.back();
 
-    ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
-        leak->backtrace.frames, leak->backtrace.max_frames);
+    ssize_t num_backtrace_frames = malloc_backtrace(
+        reinterpret_cast<void*>(it.range.begin), leak->backtrace.frames, leak->backtrace.max_frames);
     if (num_backtrace_frames > 0) {
       leak->backtrace.num_frames = num_backtrace_frames;
 
@@ -183,14 +192,13 @@
     leak->referenced_size = it.referenced_size;
     leak->total_size = leak->size + leak->referenced_size;
     memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
-        std::min(leak->size, Leak::contents_length));
+           std::min(leak->size, Leak::contents_length));
   }
 
   MEM_ALOGI("folding done");
 
-  std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
-    return a.total_size > b.total_size;
-  });
+  std::sort(leaks.begin(), leaks.end(),
+            [](const Leak& a, const Leak& b) { return a.total_size > b.total_size; });
 
   if (leaks.size() > limit) {
     leaks.resize(limit);
@@ -205,11 +213,10 @@
 }
 
 bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
-    allocator::vector<Mapping>& heap_mappings,
-    allocator::vector<Mapping>& anon_mappings,
-    allocator::vector<Mapping>& globals_mappings,
-    allocator::vector<Mapping>& stack_mappings)
-{
+                                      allocator::vector<Mapping>& heap_mappings,
+                                      allocator::vector<Mapping>& anon_mappings,
+                                      allocator::vector<Mapping>& globals_mappings,
+                                      allocator::vector<Mapping>& stack_mappings) {
   heap_mappings.clear();
   anon_mappings.clear();
   globals_mappings.clear();
@@ -245,7 +252,8 @@
       stack_mappings.emplace_back(*it);
     } else if (mapping_name.size() == 0) {
       globals_mappings.emplace_back(*it);
-    } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+    } else if (has_prefix(mapping_name, "[anon:") &&
+               mapping_name != "[anon:leak_detector_malloc]") {
       // TODO(ccross): it would be nice to treat named anonymous mappings as
       // possible leaks, but naming something in a .bss or .data section makes
       // it impossible to distinguish them from mmaped and then named mappings.
@@ -256,7 +264,7 @@
   return true;
 }
 
-template<typename T>
+template <typename T>
 static inline const char* plural(T val) {
   return (val == 1) ? "" : "s";
 }
@@ -279,6 +287,7 @@
     ThreadCapture thread_capture(parent_pid, heap);
     allocator::vector<ThreadInfo> thread_info(heap);
     allocator::vector<Mapping> mappings(heap);
+    allocator::vector<uintptr_t> refs(heap);
 
     // ptrace all the threads
     if (!thread_capture.CaptureThreads()) {
@@ -298,6 +307,11 @@
       return 1;
     }
 
+    if (!BinderReferences(refs)) {
+      continue_parent_sem.Post();
+      return 1;
+    }
+
     // malloc must be enabled to call fork, at_fork handlers take the same
     // locks as ScopedDisableMalloc.  All threads are paused in ptrace, so
     // memory state is still consistent.  Unfreeze the original thread so it
@@ -323,7 +337,7 @@
 
       MemUnreachable unreachable{parent_pid, heap};
 
-      if (!unreachable.CollectAllocations(thread_info, mappings)) {
+      if (!unreachable.CollectAllocations(thread_info, mappings, refs)) {
         _exit(2);
       }
       size_t num_allocations = unreachable.Allocations();
@@ -403,7 +417,6 @@
 }
 
 std::string Leak::ToString(bool log_contents) const {
-
   std::ostringstream oss;
 
   oss << "  " << std::dec << size;
@@ -466,23 +479,6 @@
   return oss.str();
 }
 
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
 std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
   std::ostringstream oss;
   oss << "  " << leak_bytes << " bytes in ";
@@ -492,8 +488,8 @@
   oss << std::endl;
 
   for (auto it = leaks.begin(); it != leaks.end(); it++) {
-      oss << it->ToString(log_contents);
-      oss << std::endl;
+    oss << it->ToString(log_contents);
+    oss << std::endl;
   }
 
   return oss.str();
@@ -511,9 +507,11 @@
   return info.ToString(log_contents);
 }
 
+}  // namespace android
+
 bool LogUnreachableMemory(bool log_contents, size_t limit) {
-  UnreachableMemoryInfo info;
-  if (!GetUnreachableMemory(info, limit)) {
+  android::UnreachableMemoryInfo info;
+  if (!android::GetUnreachableMemory(info, limit)) {
     return false;
   }
 
@@ -523,10 +521,9 @@
   return true;
 }
 
-
 bool NoLeaks() {
-  UnreachableMemoryInfo info;
-  if (!GetUnreachableMemory(info, 0)) {
+  android::UnreachableMemoryInfo info;
+  if (!android::GetUnreachableMemory(info, 0)) {
     return false;
   }
 
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 57b2321..9a06870 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <inttypes.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -25,6 +25,8 @@
 #include "ProcessMappings.h"
 #include "log.h"
 
+namespace android {
+
 // This function is not re-entrant since it uses a static buffer for
 // the line data.
 bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
@@ -42,8 +44,8 @@
     int name_pos;
     char perms[5];
     Mapping mapping{};
-    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n",
-        &mapping.begin, &mapping.end, perms, &name_pos) == 3) {
+    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin,
+               &mapping.end, perms, &name_pos) == 3) {
       if (perms[0] == 'r') {
         mapping.read = true;
       }
@@ -64,3 +66,5 @@
   }
   return true;
 }
+
+}  // namespace android
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
index d3b7496..a0e97e9 100644
--- a/libmemunreachable/ProcessMappings.h
+++ b/libmemunreachable/ProcessMappings.h
@@ -19,6 +19,8 @@
 
 #include "Allocator.h"
 
+namespace android {
+
 struct Mapping {
   uintptr_t begin;
   uintptr_t end;
@@ -33,4 +35,6 @@
 // the line data.
 bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
 
-#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 73b0493..aca2a82 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -23,17 +23,19 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <sys/mman.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include "android-base/macros.h"
 
+#include "PtracerThread.h"
 #include "anon_vma_naming.h"
 #include "log.h"
-#include "PtracerThread.h"
+
+namespace android {
 
 class Stack {
  public:
@@ -41,7 +43,7 @@
     int prot = PROT_READ | PROT_WRITE;
     int flags = MAP_PRIVATE | MAP_ANONYMOUS;
     page_size_ = sysconf(_SC_PAGE_SIZE);
-    size_ += page_size_*2; // guard pages
+    size_ += page_size_ * 2;  // guard pages
     base_ = mmap(NULL, size_, prot, flags, -1, 0);
     if (base_ == MAP_FAILED) {
       base_ = NULL;
@@ -52,22 +54,20 @@
     mprotect(base_, page_size_, PROT_NONE);
     mprotect(top(), page_size_, PROT_NONE);
   };
-  ~Stack() {
-    munmap(base_, size_);
-  };
+  ~Stack() { munmap(base_, size_); };
   void* top() {
     return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
   };
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Stack);
 
-  void *base_;
+  void* base_;
   size_t size_;
   size_t page_size_;
 };
 
-PtracerThread::PtracerThread(const std::function<int()>& func) :
-    child_pid_(0) {
+PtracerThread::PtracerThread(const std::function<int()>& func) : child_pid_(0) {
   stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
   if (stack_->top() == nullptr) {
     MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
@@ -93,14 +93,13 @@
   std::unique_lock<std::mutex> lk(m_);
 
   // Convert from void(*)(void*) to lambda with captures
-  auto proxy = [](void *arg) -> int {
+  auto proxy = [](void* arg) -> int {
     prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
     return (*reinterpret_cast<std::function<int()>*>(arg))();
   };
 
-  child_pid_ = clone(proxy, stack_->top(),
-       CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
-       reinterpret_cast<void*>(&func_));
+  child_pid_ = clone(proxy, stack_->top(), CLONE_VM | CLONE_FS | CLONE_FILES /*|CLONE_UNTRACED*/,
+                     reinterpret_cast<void*>(&func_));
   if (child_pid_ < 0) {
     MEM_ALOGE("failed to clone child: %s", strerror(errno));
     return false;
@@ -151,3 +150,5 @@
 void PtracerThread::ClearTracer() {
   prctl(PR_SET_PTRACER, 0);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
index f88b599..4f9c420 100644
--- a/libmemunreachable/PtracerThread.h
+++ b/libmemunreachable/PtracerThread.h
@@ -24,6 +24,8 @@
 
 #include "Allocator.h"
 
+namespace android {
+
 class Stack;
 
 // PtracerThread is similar to std::thread, except that it creates a "thread"
@@ -36,6 +38,7 @@
   ~PtracerThread();
   bool Start();
   int Join();
+
  private:
   void SetTracer(pid_t);
   void ClearTracer();
@@ -47,4 +50,6 @@
   pid_t child_pid_;
 };
 
-#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
index 287f479..bb50b9e 100644
--- a/libmemunreachable/ScopedAlarm.h
+++ b/libmemunreachable/ScopedAlarm.h
@@ -23,15 +23,15 @@
 #include <chrono>
 #include <functional>
 
+namespace android {
+
 class ScopedAlarm {
  public:
   ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
     func_ = func;
-    struct sigaction oldact{};
-    struct sigaction act{};
-    act.sa_handler = [](int) {
-      ScopedAlarm::func_();
-    };
+    struct sigaction oldact {};
+    struct sigaction act {};
+    act.sa_handler = [](int) { ScopedAlarm::func_(); };
     sigaction(SIGALRM, &act, &oldact);
 
     std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
@@ -43,11 +43,15 @@
   ~ScopedAlarm() {
     itimerval t = itimerval{};
     setitimer(ITIMER_REAL, &t, NULL);
-    struct sigaction act{};
+    struct sigaction act {};
     act.sa_handler = SIG_DFL;
     sigaction(SIGALRM, &act, NULL);
   }
+
  private:
   static std::function<void()> func_;
 };
+
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
index 758d317..655e826 100644
--- a/libmemunreachable/ScopedDisableMalloc.h
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -21,16 +21,16 @@
 
 #include "android-base/macros.h"
 
+#include "ScopedAlarm.h"
 #include "bionic.h"
 #include "log.h"
-#include "ScopedAlarm.h"
 
-class DisableMallocGuard{
+namespace android {
+
+class DisableMallocGuard {
  public:
-  DisableMallocGuard() : disabled_(false){}
-  ~DisableMallocGuard() {
-    Enable();
-  }
+  DisableMallocGuard() : disabled_(false) {}
+  ~DisableMallocGuard() { Enable(); }
 
   void Disable() {
     if (!disabled_) {
@@ -45,6 +45,7 @@
       disabled_ = false;
     }
   }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
   bool disabled_;
@@ -59,13 +60,9 @@
 // here.
 class ScopedDisableMalloc {
  public:
-  ScopedDisableMalloc() {
-    disable_malloc_.Disable();
-  }
+  ScopedDisableMalloc() { disable_malloc_.Disable(); }
 
-  ~ScopedDisableMalloc() {
-    disable_malloc_.Enable();
-  }
+  ~ScopedDisableMalloc() { disable_malloc_.Enable(); }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
@@ -74,18 +71,15 @@
 
 class ScopedDisableMallocTimeout {
  public:
-  explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
-    timeout_(timeout), timed_out_(false), disable_malloc_() {
+  explicit ScopedDisableMallocTimeout(
+      std::chrono::milliseconds timeout = std::chrono::milliseconds(2000))
+      : timeout_(timeout), timed_out_(false), disable_malloc_() {
     Disable();
   }
 
-  ~ScopedDisableMallocTimeout() {
-    Enable();
-  }
+  ~ScopedDisableMallocTimeout() { Enable(); }
 
-  bool timed_out() {
-    return timed_out_;
-  }
+  bool timed_out() { return timed_out_; }
 
   void Enable() {
     disable_malloc_.Enable();
@@ -110,4 +104,6 @@
   DisableMallocGuard disable_malloc_;
 };
 
-#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 7f44953..adabfd8 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -21,6 +21,8 @@
 
 #include "log.h"
 
+namespace android {
+
 class ScopedPipe {
  public:
   ScopedPipe() : pipefd_{-1, -1} {
@@ -29,28 +31,22 @@
       MEM_LOG_ALWAYS_FATAL("failed to open pipe");
     }
   }
-  ~ScopedPipe() {
-    Close();
-  }
+  ~ScopedPipe() { Close(); }
 
   ScopedPipe(ScopedPipe&& other) {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
   }
 
-  ScopedPipe& operator = (ScopedPipe&& other) {
+  ScopedPipe& operator=(ScopedPipe&& other) {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
     return *this;
   }
 
-  void CloseReceiver() {
-    close(ReleaseReceiver());
-  }
+  void CloseReceiver() { close(ReleaseReceiver()); }
 
-  void CloseSender() {
-    close(ReleaseSender());
-  }
+  void CloseSender() { close(ReleaseSender()); }
 
   void Close() {
     CloseReceiver();
@@ -78,4 +74,7 @@
 
   int pipefd_[2];
 };
+
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index ada2ae4..ff53fad 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -26,18 +26,18 @@
 
 #include "log.h"
 
+namespace android {
+
 class ScopedSignalHandler {
  public:
   using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
 
   explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
-  ~ScopedSignalHandler() {
-    reset();
-  }
+  ~ScopedSignalHandler() { reset(); }
 
   template <class F>
   void install(int signal, F&& f) {
-    MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+    if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
 
     handler_ = SignalFn(std::allocator_arg, allocator_,
                         [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
@@ -65,7 +65,6 @@
     }
   }
 
-
  private:
   using SignalFn = std::function<void(int, siginfo_t*, void*)>;
   DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
@@ -77,4 +76,6 @@
   static SignalFn handler_;
 };
 
-#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
index 6bcf4ea..cd73972 100644
--- a/libmemunreachable/Semaphore.h
+++ b/libmemunreachable/Semaphore.h
@@ -22,6 +22,8 @@
 
 #include "android-base/macros.h"
 
+namespace android {
+
 class Semaphore {
  public:
   explicit Semaphore(int count = 0) : count_(count) {}
@@ -29,7 +31,7 @@
 
   void Wait(std::chrono::milliseconds ms) {
     std::unique_lock<std::mutex> lk(m_);
-    cv_.wait_for(lk, ms, [&]{
+    cv_.wait_for(lk, ms, [&] {
       if (count_ > 0) {
         count_--;
         return true;
@@ -44,6 +46,7 @@
     }
     cv_.notify_one();
   }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Semaphore);
 
@@ -52,5 +55,6 @@
   std::condition_variable cv_;
 };
 
+}  // namespace android
 
-#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
+#endif  // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 2546341..355679f 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -24,7 +24,9 @@
 
 #include "Allocator.h"
 
-template<class T>
+namespace android {
+
+template <class T>
 class Node {
  public:
   allocator::set<Node<T>*> references_in;
@@ -34,39 +36,41 @@
 
   T* ptr;
 
-  Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
-      ptr(ptr) {};
+  Node(T* ptr, Allocator<Node> allocator)
+      : references_in(allocator), references_out(allocator), ptr(ptr){};
   Node(Node&& rhs) = default;
   void Edge(Node<T>* ref) {
     references_out.emplace(ref);
     ref->references_in.emplace(this);
   }
-  template<class F>
+  template <class F>
   void Foreach(F&& f) {
-    for (auto& node: references_out) {
+    for (auto& node : references_out) {
       f(node->ptr);
     }
   }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Node<T>);
 };
 
-template<class T>
+template <class T>
 using Graph = allocator::vector<Node<T>*>;
 
-template<class T>
+template <class T>
 using SCC = allocator::vector<Node<T>*>;
 
-template<class T>
+template <class T>
 using SCCList = allocator::vector<SCC<T>>;
 
-template<class T>
+template <class T>
 class TarjanAlgorithm {
  public:
-  explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0),
-    stack_(allocator), components_(allocator) {}
+  explicit TarjanAlgorithm(Allocator<void> allocator)
+      : index_(0), stack_(allocator), components_(allocator) {}
 
   void Execute(Graph<T>& graph, SCCList<T>& out);
+
  private:
   static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
   void Tarjan(Node<T>* vertex, Graph<T>& graph);
@@ -76,17 +80,17 @@
   SCCList<T> components_;
 };
 
-template<class T>
+template <class T>
 void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
   stack_.clear();
   components_.clear();
   index_ = 0;
-  for (auto& it: graph) {
+  for (auto& it : graph) {
     it->index = UNDEFINED_INDEX;
     it->lowlink = UNDEFINED_INDEX;
   }
 
-  for (auto& it: graph) {
+  for (auto& it : graph) {
     if (it->index == UNDEFINED_INDEX) {
       Tarjan(it, graph);
     }
@@ -94,14 +98,14 @@
   out.swap(components_);
 }
 
-template<class T>
+template <class T>
 void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
   assert(vertex->index == UNDEFINED_INDEX);
   vertex->index = index_;
   vertex->lowlink = index_;
   index_++;
   stack_.push_back(vertex);
-  for (auto& it: vertex->references_out) {
+  for (auto& it : vertex->references_out) {
     Node<T>* vertex_next = it;
     if (vertex_next->index == UNDEFINED_INDEX) {
       Tarjan(vertex_next, graph);
@@ -123,10 +127,12 @@
   }
 }
 
-template<class T>
+template <class T>
 void Tarjan(Graph<T>& graph, SCCList<T>& out) {
   TarjanAlgorithm<T> tarjan{graph.get_allocator()};
   tarjan.Execute(graph, out);
 }
 
-#endif // LIBMEMUNREACHABLE_TARJAN_H_
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 3891f2d..45eb55d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -21,13 +21,13 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <map>
 #include <memory>
@@ -39,6 +39,8 @@
 #include "Allocator.h"
 #include "log.h"
 
+namespace android {
+
 // bionic interfaces used:
 // atoi
 // strlcat
@@ -50,12 +52,12 @@
 // Convert a pid > 0 to a string.  sprintf might allocate, so we can't use it.
 // Returns a pointer somewhere in buf to a null terminated string, or NULL
 // on error.
-static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+static char* pid_to_str(char* buf, size_t len, pid_t pid) {
   if (pid <= 0) {
     return nullptr;
   }
 
-  char *ptr = buf + len - 1;
+  char* ptr = buf + len - 1;
   *ptr = 0;
   while (pid > 0) {
     ptr--;
@@ -79,6 +81,7 @@
   bool ReleaseThread(pid_t tid);
   bool CapturedThreadInfo(ThreadInfoList& threads);
   void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+
  private:
   int CaptureThread(pid_t tid);
   bool ReleaseThread(pid_t tid, unsigned int signal);
@@ -92,9 +95,8 @@
   std::function<void(pid_t)> inject_test_func_;
 };
 
-ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
-    captured_threads_(allocator), allocator_(allocator), pid_(pid) {
-}
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator)
+    : captured_threads_(allocator), allocator_(allocator), pid_(pid) {}
 
 bool ThreadCaptureImpl::ListThreads(TidList& tids) {
   tids.clear();
@@ -115,11 +117,11 @@
   }
 
   struct linux_dirent64 {
-    uint64_t  d_ino;
-    int64_t   d_off;
-    uint16_t  d_reclen;
-    char      d_type;
-    char      d_name[];
+    uint64_t d_ino;
+    int64_t d_off;
+    uint16_t d_reclen;
+    char d_type;
+    char d_name[];
   } __attribute((packed));
   char dirent_buf[4096];
   ssize_t nread;
@@ -209,7 +211,7 @@
 bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
   thread_info.tid = tid;
 
-  const unsigned int max_num_regs = 128; // larger than number of registers on any device
+  const unsigned int max_num_regs = 128;  // larger than number of registers on any device
   uintptr_t regs[max_num_regs];
   struct iovec iovec;
   iovec.iov_base = &regs;
@@ -243,7 +245,7 @@
 
   thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
 
-   return true;
+  return true;
 }
 
 int ThreadCaptureImpl::CaptureThread(pid_t tid) {
@@ -266,7 +268,7 @@
 
   unsigned int resume_signal = 0;
 
-  unsigned int signal =  WSTOPSIG(status);
+  unsigned int signal = WSTOPSIG(status);
   if ((status >> 16) == PTRACE_EVENT_STOP) {
     switch (signal) {
       case SIGSTOP:
@@ -307,7 +309,7 @@
 
 bool ThreadCaptureImpl::ReleaseThreads() {
   bool ret = true;
-  for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+  for (auto it = captured_threads_.begin(); it != captured_threads_.end();) {
     if (ReleaseThread(it->first, it->second)) {
       it = captured_threads_.erase(it);
     } else {
@@ -361,3 +363,5 @@
 void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
   impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
 }
+
+}  // namespace android
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
index 1022cad..961cb60 100644
--- a/libmemunreachable/ThreadCapture.h
+++ b/libmemunreachable/ThreadCapture.h
@@ -21,6 +21,8 @@
 
 #include "Allocator.h"
 
+namespace android {
+
 struct ThreadInfo {
   pid_t tid;
   allocator::vector<uintptr_t> regs;
@@ -33,7 +35,7 @@
 class ThreadCaptureImpl;
 
 class ThreadCapture {
-public:
+ public:
   ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
   ~ThreadCapture();
 
@@ -44,11 +46,13 @@
   bool CapturedThreadInfo(ThreadInfoList& threads);
   void InjectTestFunc(std::function<void(pid_t)>&& f);
 
-private:
+ private:
   ThreadCapture(const ThreadCapture&) = delete;
   void operator=(const ThreadCapture&) = delete;
 
   Allocator<ThreadCaptureImpl>::unique_ptr impl_;
 };
 
+}  // namespace android
+
 #endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
index 1e4ade1..fb31e41 100644
--- a/libmemunreachable/anon_vma_naming.h
+++ b/libmemunreachable/anon_vma_naming.h
@@ -19,7 +19,7 @@
 
 #include <sys/prctl.h>
 
-#define PR_SET_VMA            0x53564d41
-#define  PR_SET_VMA_ANON_NAME 0
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
 
-#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#endif  // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
index 83d07a8..dd1ec79 100644
--- a/libmemunreachable/bionic.h
+++ b/libmemunreachable/bionic.h
@@ -17,9 +17,9 @@
 #ifndef LIBMEMUNREACHABLE_BIONIC_H_
 #define LIBMEMUNREACHABLE_BIONIC_H_
 
-#include <sys/cdefs.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -27,9 +27,9 @@
 extern void malloc_disable();
 extern void malloc_enable();
 extern int malloc_iterate(uintptr_t base, size_t size,
-    void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+                          void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
 extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
 
 __END_DECLS
 
-#endif // LIBMEMUNREACHABLE_BIONIC_H_
+#endif  // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 9b227fd..438fcaf 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -17,12 +17,15 @@
 #ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
 #define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
 
+#include <string.h>
 #include <sys/cdefs.h>
 
 #ifdef __cplusplus
 
-#include <vector>
 #include <string>
+#include <vector>
+
+namespace android {
 
 struct Leak {
   uintptr_t begin;
@@ -73,6 +76,8 @@
 
 std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
 
+}  // namespace android
+
 #endif
 
 __BEGIN_DECLS
@@ -83,4 +88,4 @@
 
 __END_DECLS
 
-#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#endif  // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index 1725c53..44c5f85 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -26,13 +26,21 @@
 #define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
 #define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
 #define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
-#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV_IMPL(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#ifdef NDEBUG
+#define MEM_ALOGV(...)             \
+  do {                             \
+    if (0) {                       \
+      MEM_ALOGV_IMPL(__VA_ARGS__); \
+    }                              \
+  } while (0)
+#else
+#define MEM_ALOGV(...) MEM_ALOGV_IMPL(__VA_ARGS__)
+#endif
 
 #define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
 
-#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
-  ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
-
 #else
 
 #include <log/log.h>
@@ -43,8 +51,7 @@
 #define MEM_ALOGI ALOGI
 
 #define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
-#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
 
 #endif
 
-#endif // LIBMEMUNREACHABLE_LOG_H_
+#endif  // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index 21c8218..8991a7b 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -16,44 +16,44 @@
 
 #include <Allocator.h>
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 
+namespace android {
 
 std::function<void()> ScopedAlarm::func_;
 
 class AllocatorTest : public testing::Test {
  protected:
   AllocatorTest() : heap(), disable_malloc_() {}
-  virtual void SetUp() {
-    heap_count = 0;
-  }
+  virtual void SetUp() { heap_count = 0; }
   virtual void TearDown() {
     ASSERT_EQ(heap_count, 0);
     ASSERT_TRUE(heap.empty());
     ASSERT_FALSE(disable_malloc_.timed_out());
   }
   Heap heap;
+
  private:
   ScopedDisableMallocTimeout disable_malloc_;
 };
 
 TEST_F(AllocatorTest, simple) {
   Allocator<char[100]> allocator(heap);
-  void *ptr = allocator.allocate();
+  void* ptr = allocator.allocate();
   ASSERT_TRUE(ptr != NULL);
   allocator.deallocate(ptr);
 }
 
 TEST_F(AllocatorTest, multiple) {
   Allocator<char[100]> allocator(heap);
-  void *ptr1 = allocator.allocate();
+  void* ptr1 = allocator.allocate();
   ASSERT_TRUE(ptr1 != NULL);
-  void *ptr2 = allocator.allocate();
+  void* ptr2 = allocator.allocate();
   ASSERT_TRUE(ptr2 != NULL);
   ASSERT_NE(ptr1, ptr2);
   allocator.deallocate(ptr1);
-  void *ptr3 = allocator.allocate();
+  void* ptr3 = allocator.allocate();
   ASSERT_EQ(ptr1, ptr3);
   allocator.deallocate(ptr3);
   allocator.deallocate(ptr2);
@@ -63,7 +63,7 @@
   const int num = 4096;
   const int size = 128;
   Allocator<char[size]> allocator(heap);
-  void *ptr[num];
+  void* ptr[num];
   for (int i = 0; i < num; i++) {
     ptr[i] = allocator.allocate();
     memset(ptr[i], 0xaa, size);
@@ -87,7 +87,7 @@
 TEST_F(AllocatorTest, large) {
   const size_t size = 1024 * 1024;
   Allocator<char[size]> allocator(heap);
-  void *ptr = allocator.allocate();
+  void* ptr = allocator.allocate();
   memset(ptr, 0xaa, size);
   allocator.deallocate(ptr);
 }
@@ -96,7 +96,7 @@
   const int num = 128;
   const int size = 1024 * 1024;
   Allocator<char[size]> allocator(heap);
-  void *ptr[num];
+  void* ptr[num];
   for (int i = 0; i < num; i++) {
     ptr[i] = allocator.allocate();
     memset(ptr[i], 0xaa, size);
@@ -172,3 +172,5 @@
 
   ASSERT_NE(ptr, nullptr);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/AndroidTest.xml b/libmemunreachable/tests/AndroidTest.xml
new file mode 100644
index 0000000..604c0ec
--- /dev/null
+++ b/libmemunreachable/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for memunreachable_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="memunreachable_test->/data/local/tmp/memunreachable_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="memunreachable_test" />
+    </test>
+</configuration>
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
new file mode 100644
index 0000000..6e85d5a
--- /dev/null
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gtest/gtest.h>
+
+#include "Allocator.h"
+#include "Binder.h"
+
+namespace android {
+
+static const String16 service_name("test.libmemunreachable_binder");
+
+class BinderService : public BBinder {
+ public:
+  BinderService() = default;
+  virtual ~BinderService() = default;
+
+  virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply,
+                              uint32_t /*flags*/ = 0) {
+    reply->writeStrongBinder(ref);
+    ref = data.readStrongBinder();
+    return 0;
+  }
+
+ private:
+  sp<IBinder> ref;
+};
+
+class BinderObject : public BBinder {
+ public:
+  BinderObject() = default;
+  ~BinderObject() = default;
+};
+
+class ServiceProcess {
+ public:
+  ServiceProcess() : child_(0) {}
+  ~ServiceProcess() { Stop(); }
+
+  bool Run() {
+    pid_t ret = fork();
+    if (ret < 0) {
+      return false;
+    } else if (ret == 0) {
+      // child
+      _exit(Service());
+    } else {
+      // parent
+      child_ = ret;
+      return true;
+    }
+  }
+
+  bool Stop() {
+    if (child_ > 0) {
+      if (kill(child_, SIGTERM)) {
+        return false;
+      }
+      int status = 0;
+      if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) {
+        return false;
+      }
+      child_ = 0;
+      return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+    }
+
+    return true;
+  }
+
+  int Service() {
+    sp<ProcessState> proc{ProcessState::self()};
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+      fprintf(stderr, "Failed to get service manager\n");
+      return 1;
+    }
+    if (sm->addService(service_name, new BinderService()) != OK) {
+      fprintf(stderr, "Failed to add test service\n");
+      return 1;
+    }
+    proc->startThreadPool();
+    pause();
+    return 0;
+  }
+
+ private:
+  pid_t child_;
+};
+
+class BinderTest : public ::testing::Test {
+ protected:
+  ServiceProcess service_process_;
+};
+
+TEST_F(BinderTest, binder) {
+  ServiceProcess service_process;
+  ASSERT_TRUE(service_process.Run());
+
+  sp<IServiceManager> sm = defaultServiceManager();
+  ASSERT_TRUE(sm != nullptr);
+
+  // A small sleep allows the service to start, which
+  // prevents a longer sleep in getService.
+  usleep(100000);
+
+  sp<IBinder> service = sm->getService(service_name);
+  ASSERT_TRUE(service != nullptr);
+
+  sp<IBinder> binder{new BinderObject()};
+
+  Parcel send;
+  Parcel reply;
+
+  send.writeStrongBinder(binder);
+  status_t rv = service->transact(0, send, &reply);
+  ASSERT_EQ(static_cast<status_t>(OK), rv);
+
+  Heap heap;
+  allocator::vector<uintptr_t> refs{heap};
+
+  ASSERT_TRUE(BinderReferences(refs));
+
+  bool found_ref = false;
+  for (auto ref : refs) {
+    if (ref == reinterpret_cast<uintptr_t>(binder.get())) {
+      found_ref = true;
+    }
+  }
+
+  ASSERT_TRUE(found_ref);
+}
+
+}  // namespace android
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index 4e6155b..f446719 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -19,11 +19,13 @@
 #include <chrono>
 #include <functional>
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 
 using namespace std::chrono_literals;
 
+namespace android {
+
 class DisableMallocTest : public ::testing::Test {
  protected:
   void alarm(std::chrono::microseconds us) {
@@ -36,75 +38,84 @@
 };
 
 TEST_F(DisableMallocTest, reenable) {
-  ASSERT_EXIT({
-    alarm(100ms);
-    void *ptr1 = malloc(128);
-    ASSERT_NE(ptr1, nullptr);
-    free(ptr1);
-    {
-      ScopedDisableMalloc disable_malloc;
-    }
-    void *ptr2 = malloc(128);
-    ASSERT_NE(ptr2, nullptr);
-    free(ptr2);
-    _exit(1);
-  }, ::testing::ExitedWithCode(1), "");
+  ASSERT_EXIT(
+      {
+        alarm(100ms);
+        void* ptr1 = malloc(128);
+        ASSERT_NE(ptr1, nullptr);
+        free(ptr1);
+        { ScopedDisableMalloc disable_malloc; }
+        void* ptr2 = malloc(128);
+        ASSERT_NE(ptr2, nullptr);
+        free(ptr2);
+        _exit(1);
+      },
+      ::testing::ExitedWithCode(1), "");
 }
 
 TEST_F(DisableMallocTest, deadlock_allocate) {
-  ASSERT_DEATH({
-    void *ptr = malloc(128);
-    ASSERT_NE(ptr, nullptr);
-    free(ptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      void* ptr = malloc(128);
-      ASSERT_NE(ptr, nullptr);
-      free(ptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        void* ptr = malloc(128);
+        ASSERT_NE(ptr, nullptr);
+        free(ptr);
+        {
+          alarm(100ms);
+          ScopedDisableMalloc disable_malloc;
+          void* ptr = malloc(128);
+          ASSERT_NE(ptr, nullptr);
+          free(ptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_new) {
-  ASSERT_DEATH({
-    char* ptr = new(char);
-    ASSERT_NE(ptr, nullptr);
-    delete(ptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      char* ptr = new (std::nothrow)(char);
-      ASSERT_NE(ptr, nullptr);
-      delete(ptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        // C++ allows `new Foo` to be replaced with a stack allocation or merged
+        // with future `new Foo` expressions, provided certain conditions are
+        // met [expr.new/10]. None of this applies to `operator new(size_t)`.
+        void* ptr = ::operator new(1);
+        ASSERT_NE(ptr, nullptr);
+        ::operator delete(ptr);
+        {
+          alarm(100ms);
+          ScopedDisableMalloc disable_malloc;
+          void* ptr = ::operator new(1);
+          ASSERT_NE(ptr, nullptr);
+          ::operator delete(ptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_delete) {
-  ASSERT_DEATH({
-    char* ptr = new(char);
-    ASSERT_NE(ptr, nullptr);
-    {
-      alarm(250ms);
-      ScopedDisableMalloc disable_malloc;
-      delete(ptr);
-      // Force ptr usage or this code gets optimized away by the arm64 compiler.
-      ASSERT_NE(ptr, nullptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        void* ptr = ::operator new(1);
+        ASSERT_NE(ptr, nullptr);
+        {
+          alarm(250ms);
+          ScopedDisableMalloc disable_malloc;
+          ::operator delete(ptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_free) {
-  ASSERT_DEATH({
-    void *ptr = malloc(128);
-    ASSERT_NE(ptr, nullptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      free(ptr);
-    }
-  }, "");
+  ASSERT_DEATH(
+      {
+        void* ptr = malloc(128);
+        ASSERT_NE(ptr, nullptr);
+        {
+          alarm(100ms);
+          ScopedDisableMalloc disable_malloc;
+          free(ptr);
+        }
+      },
+      "");
 }
 
 TEST_F(DisableMallocTest, deadlock_fork) {
@@ -113,6 +124,8 @@
       alarm(100ms);
       ScopedDisableMalloc disable_malloc;
       fork();
-    }
-  }, "");
 }
+}, "");
+}
+
+}  // namespace android
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 98e4aa1..84a0ec6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -19,10 +19,12 @@
 
 #include "HeapWalker.h"
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 #include "Allocator.h"
 
+namespace android {
+
 class HeapWalkerTest : public ::testing::Test {
  public:
   HeapWalkerTest() : disable_malloc_(), heap_() {}
@@ -172,20 +174,20 @@
   ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(2U, num_leaks);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
 }
 
 TEST_F(HeapWalkerTest, segv) {
   const size_t page_size = sysconf(_SC_PAGE_SIZE);
-  void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+  void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
   ASSERT_NE(buffer1, nullptr);
   void* buffer2;
 
   buffer2 = &buffer1;
 
   HeapWalker heap_walker(heap_);
-  heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1)+page_size);
+  heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1) + page_size);
   heap_walker.Root(buffer_begin(buffer2), buffer_end(buffer2));
 
   ASSERT_EQ(true, heap_walker.DetectLeaks());
@@ -199,3 +201,5 @@
   EXPECT_EQ(0U, leaked_bytes);
   ASSERT_EQ(0U, leaked.size());
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
index a7e3f07..0ef0487 100644
--- a/libmemunreachable/tests/HostMallocStub.cpp
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -16,8 +16,6 @@
 
 #include "bionic.h"
 
-void malloc_disable() {
-}
+void malloc_disable() {}
 
-void malloc_enable() {
-}
+void malloc_enable() {}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
index e85df5f..f5b3631 100644
--- a/libmemunreachable/tests/LeakFolding_test.cpp
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-#include "HeapWalker.h"
 #include "LeakFolding.h"
+#include "HeapWalker.h"
 
-#include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
+#include <gtest/gtest.h>
 #include "Allocator.h"
 
+namespace android {
+
 class LeakFoldingTest : public ::testing::Test {
  public:
   LeakFoldingTest() : disable_malloc_(), heap_() {}
@@ -84,7 +86,7 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(2U, num_leaks);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
   EXPECT_EQ(0U, leaked[0].referenced_count);
   EXPECT_EQ(0U, leaked[0].referenced_size);
@@ -113,7 +115,7 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(2U, num_leaks);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(1U, leaked.size());
   EXPECT_EQ(1U, leaked[0].referenced_count);
   EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -144,10 +146,10 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(3U, num_leaks);
-  EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(3 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(1U, leaked.size());
   EXPECT_EQ(2U, leaked[0].referenced_count);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
 }
 
 TEST_F(LeakFoldingTest, dominator_cycle) {
@@ -175,13 +177,13 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(3U, num_leaks);
-  EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
 
   EXPECT_EQ(2U, leaked[0].referenced_count);
-  EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
   EXPECT_EQ(2U, leaked[1].referenced_count);
-  EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+  EXPECT_EQ(3 * sizeof(uintptr_t), leaked[1].referenced_size);
 }
 
 TEST_F(LeakFoldingTest, two_cycles) {
@@ -218,12 +220,12 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(6U, num_leaks);
-  EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(6 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(2U, leaked.size());
   EXPECT_EQ(2U, leaked[0].referenced_count);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked[0].referenced_size);
   EXPECT_EQ(2U, leaked[1].referenced_count);
-  EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+  EXPECT_EQ(2 * sizeof(uintptr_t), leaked[1].referenced_size);
 }
 
 TEST_F(LeakFoldingTest, two_dominator_cycles) {
@@ -254,7 +256,7 @@
   ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
 
   EXPECT_EQ(4U, num_leaks);
-  EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+  EXPECT_EQ(4 * sizeof(uintptr_t), leaked_bytes);
   ASSERT_EQ(4U, leaked.size());
   EXPECT_EQ(1U, leaked[0].referenced_count);
   EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
@@ -272,13 +274,13 @@
 
   HeapWalker heap_walker(heap_);
 
-  for (size_t i = 0; i < n; i ++) {
+  for (size_t i = 0; i < n; i++) {
     ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
-        reinterpret_cast<uintptr_t>(&buffer[i+1])));
+                                       reinterpret_cast<uintptr_t>(&buffer[i + 1])));
   }
 
   for (size_t i = 0; i < n - 1; i++) {
-    buffer[i] = &buffer[i+1];
+    buffer[i] = &buffer[i + 1];
   }
   buffer[n - 1] = &buffer[0];
 
@@ -306,15 +308,15 @@
   HeapWalker heap_walker(heap_);
 
   for (size_t i = 0; i < n - 1; i++) {
-    buffer[i] = &buffer[i+1];
+    buffer[i] = &buffer[i + 1];
   }
   buffer[n - 1] = &buffer[0];
 
   buffer1[0] = &buffer[0];
 
-  for (size_t i = 0; i < n; i ++) {
+  for (size_t i = 0; i < n; i++) {
     ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
-        reinterpret_cast<uintptr_t>(&buffer[i+1])));
+                                       reinterpret_cast<uintptr_t>(&buffer[i + 1])));
   }
 
   ALLOCATION(heap_walker, buffer1);
@@ -425,3 +427,5 @@
   EXPECT_EQ(3U, leaked[3].referenced_count);
   EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 71da365..87417f1 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -16,36 +16,35 @@
 
 #include <fcntl.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <sys/prctl.h>
+#include <unistd.h>
 
 #include <gtest/gtest.h>
 
 #include <memunreachable/memunreachable.h>
 
+namespace android {
+
 class HiddenPointer {
  public:
-  explicit HiddenPointer(size_t size = 256) {
-    Set(malloc(size));
-  }
-  ~HiddenPointer() {
-    Free();
-  }
-  void* Get() {
-    return reinterpret_cast<void*>(~ptr_);
-  }
+  // Since we're doing such a good job of hiding it, the static analyzer
+  // thinks that we're leaking this `malloc`. This is probably related to
+  // https://bugs.llvm.org/show_bug.cgi?id=34198. NOLINTNEXTLINE
+  explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
+  ~HiddenPointer() { Free(); }
+  void* Get() { return reinterpret_cast<void*>(~ptr_); }
   void Free() {
     free(Get());
     Set(nullptr);
   }
+
  private:
-  void Set(void* ptr) {
-    ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
-  }
+  void Set(void* ptr) { ptr_ = ~reinterpret_cast<uintptr_t>(ptr); }
   volatile uintptr_t ptr_;
 };
 
-static void Ref(void* ptr) {
+// Trick the compiler into thinking a value on the stack is still referenced.
+static void Ref(void** ptr) {
   write(0, ptr, 0);
 }
 
@@ -63,14 +62,14 @@
 
   {
     void* ptr = hidden_ptr.Get();
-    Ref(ptr);
+    Ref(&ptr);
 
     UnreachableMemoryInfo info;
 
     ASSERT_TRUE(GetUnreachableMemory(info));
     ASSERT_EQ(0U, info.leaks.size());
 
-    Ref(ptr);
+    ptr = nullptr;
   }
 
   {
@@ -216,3 +215,5 @@
 
   ASSERT_TRUE(LogUnreachableMemory(true, 100));
 }
+
+}  // namespace android
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 44aabd7..4fbf729 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -34,6 +34,8 @@
 
 using namespace std::chrono_literals;
 
+namespace android {
+
 class ThreadListTest : public ::testing::TestWithParam<int> {
  public:
   ThreadListTest() : stop_(false) {}
@@ -45,12 +47,10 @@
     WaitForThreads();
   }
 
-  virtual void TearDown() {
-    ASSERT_TRUE(heap.empty());
-  }
+  virtual void TearDown() { ASSERT_TRUE(heap.empty()); }
 
  protected:
-  template<class Function>
+  template <class Function>
   void StartThreads(unsigned int threads, Function&& func) {
     threads_.reserve(threads);
     tids_.reserve(threads);
@@ -68,14 +68,14 @@
 
         {
           std::unique_lock<std::mutex> lk(m_);
-          cv_stop_.wait(lk, [&] {return stop_;});
+          cv_stop_.wait(lk, [&] { return stop_; });
         }
       });
     }
 
     {
       std::unique_lock<std::mutex> lk(m_);
-      cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+      cv_start_.wait(lk, [&] { return tids_.size() == threads; });
     }
   }
 
@@ -93,9 +93,7 @@
     tids_.clear();
   }
 
-  std::vector<pid_t>& tids() {
-    return tids_;
-  }
+  std::vector<pid_t>& tids() { return tids_; }
 
   Heap heap;
 
@@ -143,7 +141,7 @@
 TEST_P(ThreadListTest, list_some) {
   const unsigned int threads = GetParam() - 1;
 
-  StartThreads(threads, [](){});
+  StartThreads(threads, []() {});
   std::vector<pid_t> expected_tids = tids();
   expected_tids.push_back(getpid());
 
@@ -176,10 +174,8 @@
  public:
   ThreadCaptureTest() {}
   ~ThreadCaptureTest() {}
-  void Fork(std::function<void()>&& child_init,
-      std::function<void()>&& child_cleanup,
-      std::function<void(pid_t)>&& parent) {
-
+  void Fork(std::function<void()>&& child_init, std::function<void()>&& child_cleanup,
+            std::function<void(pid_t)>&& parent) {
     ScopedPipe start_pipe;
     ScopedPipe stop_pipe;
 
@@ -211,39 +207,40 @@
 TEST_P(ThreadCaptureTest, capture_some) {
   const unsigned int threads = GetParam();
 
-  Fork([&](){
-    // child init
-    StartThreads(threads - 1, [](){});
-  },
-  [&](){
-    // child cleanup
-    StopThreads();
-  },
-  [&](pid_t child){
-    // parent
-    ASSERT_GT(child, 0);
+  Fork(
+      [&]() {
+        // child init
+        StartThreads(threads - 1, []() {});
+      },
+      [&]() {
+        // child cleanup
+        StopThreads();
+      },
+      [&](pid_t child) {
+        // parent
+        ASSERT_GT(child, 0);
 
-    {
-      ScopedDisableMallocTimeout disable_malloc;
+        {
+          ScopedDisableMallocTimeout disable_malloc;
 
-      ThreadCapture thread_capture(child, heap);
-      auto list_tids = allocator::vector<pid_t>(heap);
+          ThreadCapture thread_capture(child, heap);
+          auto list_tids = allocator::vector<pid_t>(heap);
 
-      ASSERT_TRUE(thread_capture.ListThreads(list_tids));
-      ASSERT_EQ(threads, list_tids.size());
+          ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+          ASSERT_EQ(threads, list_tids.size());
 
-      ASSERT_TRUE(thread_capture.CaptureThreads());
+          ASSERT_TRUE(thread_capture.CaptureThreads());
 
-      auto thread_info = allocator::vector<ThreadInfo>(heap);
-      ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
-      ASSERT_EQ(threads, thread_info.size());
-      ASSERT_TRUE(thread_capture.ReleaseThreads());
+          auto thread_info = allocator::vector<ThreadInfo>(heap);
+          ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+          ASSERT_EQ(threads, thread_info.size());
+          ASSERT_TRUE(thread_capture.ReleaseThreads());
 
-      if (!HasFailure()) {
-        ASSERT_FALSE(disable_malloc.timed_out());
-      }
-}
-  });
+          if (!HasFailure()) {
+            ASSERT_FALSE(disable_malloc.timed_out());
+          }
+        }
+      });
 }
 
 INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
@@ -262,7 +259,7 @@
       ScopedDisableMallocTimeout disable_malloc;
 
       ThreadCapture thread_capture(ret, heap);
-      thread_capture.InjectTestFunc([&](pid_t tid){
+      thread_capture.InjectTestFunc([&](pid_t tid) {
         syscall(SYS_tgkill, ret, tid, SIGKILL);
         usleep(10000);
       });
@@ -288,62 +285,65 @@
   // For signal handler
   static ScopedPipe* g_pipe;
 
-  Fork([&](){
-    // child init
-    pipe.CloseReceiver();
+  Fork(
+      [&]() {
+        // child init
+        pipe.CloseReceiver();
 
-    g_pipe = &pipe;
+        g_pipe = &pipe;
 
-    struct sigaction act{};
-    act.sa_handler = [](int){
-      char buf = '+';
-      write(g_pipe->Sender(), &buf, 1);
-      g_pipe->CloseSender();
-    };
-    sigaction(sig, &act, NULL);
-    sigset_t set;
-    sigemptyset(&set);
-    sigaddset(&set, sig);
-    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
-  },
-  [&](){
-    // child cleanup
-    g_pipe = nullptr;
-    pipe.Close();
-  },
-  [&](pid_t child){
-    // parent
-    ASSERT_GT(child, 0);
-    pipe.CloseSender();
+        struct sigaction act {};
+        act.sa_handler = [](int) {
+          char buf = '+';
+          write(g_pipe->Sender(), &buf, 1);
+          g_pipe->CloseSender();
+        };
+        sigaction(sig, &act, NULL);
+        sigset_t set;
+        sigemptyset(&set);
+        sigaddset(&set, sig);
+        pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+      },
+      [&]() {
+        // child cleanup
+        g_pipe = nullptr;
+        pipe.Close();
+      },
+      [&](pid_t child) {
+        // parent
+        ASSERT_GT(child, 0);
+        pipe.CloseSender();
 
-    {
-      ScopedDisableMallocTimeout disable_malloc;
+        {
+          ScopedDisableMallocTimeout disable_malloc;
 
-      ThreadCapture thread_capture(child, heap);
-      thread_capture.InjectTestFunc([&](pid_t tid){
-        syscall(SYS_tgkill, child, tid, sig);
-        usleep(10000);
+          ThreadCapture thread_capture(child, heap);
+          thread_capture.InjectTestFunc([&](pid_t tid) {
+            syscall(SYS_tgkill, child, tid, sig);
+            usleep(10000);
+          });
+          auto list_tids = allocator::vector<pid_t>(heap);
+
+          ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+          ASSERT_EQ(1U, list_tids.size());
+
+          ASSERT_TRUE(thread_capture.CaptureThreads());
+
+          auto thread_info = allocator::vector<ThreadInfo>(heap);
+          ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+          ASSERT_EQ(1U, thread_info.size());
+          ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+          usleep(100000);
+          char buf;
+          ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+          ASSERT_EQ(buf, '+');
+
+          if (!HasFailure()) {
+            ASSERT_FALSE(disable_malloc.timed_out());
+          }
+        }
       });
-      auto list_tids = allocator::vector<pid_t>(heap);
-
-      ASSERT_TRUE(thread_capture.ListThreads(list_tids));
-      ASSERT_EQ(1U, list_tids.size());
-
-      ASSERT_TRUE(thread_capture.CaptureThreads());
-
-      auto thread_info = allocator::vector<ThreadInfo>(heap);
-      ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
-      ASSERT_EQ(1U, thread_info.size());
-      ASSERT_TRUE(thread_capture.ReleaseThreads());
-
-      usleep(100000);
-      char buf;
-      ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
-      ASSERT_EQ(buf, '+');
-
-      if (!HasFailure()) {
-        ASSERT_FALSE(disable_malloc.timed_out());
-      }
-    }
-  });
 }
+
+}  // namespace android
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 38859d1..6549b8d 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -7,7 +7,6 @@
 cc_defaults {
     name: "metricslogger_defaults",
 
-    clang: true,
     host_supported: true,
 
     export_include_dirs: ["include"],
@@ -19,11 +18,6 @@
         "-Wall",
         "-Wextra",
         "-Werror",
-
-        // 524291 corresponds to sysui_histogram, from
-        // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
-        "-DHISTOGRAM_LOG_TAG=524292",
-        "-DCOUNT_LOG_TAG=524290",
     ],
 }
 
@@ -31,7 +25,6 @@
 // -----------------------------------------------------------------------------
 cc_library_shared {
     name: "libmetricslogger",
-    vendor_available: true,
     srcs: metricslogger_lib_src_files,
     defaults: ["metricslogger_defaults"],
 }
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 36e124d..189bc4b 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -28,14 +28,26 @@
 // log buffer.
 void LogCounter(const std::string& name, int32_t val);
 
+// Logs a Tron multi_action with category|category| containing the string
+// |value| in the field |field|.
+void LogMultiAction(int32_t category, int32_t field, const std::string& value);
+
 // TODO: replace these with the metric_logger.proto definitions
 enum {
     LOGBUILDER_CATEGORY = 757,
+    LOGBUILDER_TYPE = 758,
     LOGBUILDER_NAME = 799,
     LOGBUILDER_BUCKET = 801,
     LOGBUILDER_VALUE = 802,
     LOGBUILDER_COUNTER = 803,
     LOGBUILDER_HISTOGRAM = 804,
+
+    ACTION_BOOT = 1098,
+    FIELD_PLATFORM_REASON = 1099,
+};
+
+enum {
+    TYPE_ACTION = 4,
 };
 
 }  // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6f65e10..fdc4407 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,24 +18,40 @@
 
 #include <cstdlib>
 
+#include <log/event_tag_map.h>
 #include <log/log_event_list.h>
 
+namespace {
+
+EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
+const int kSysuiMultiActionTag = android_lookupEventTagNum(
+    kEventTagMap, "sysui_multi_action", "(content|4)", ANDROID_LOG_UNKNOWN);
+
+}  // namespace
+
 namespace android {
 namespace metricslogger {
 
 // Mirror com.android.internal.logging.MetricsLogger#histogram().
 void LogHistogram(const std::string& event, int32_t data) {
-    android_log_event_list log(HISTOGRAM_LOG_TAG);
+    android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
         << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#count().
 void LogCounter(const std::string& name, int32_t val) {
-    android_log_event_list log(COUNT_LOG_TAG);
+    android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
         << val << LOG_ID_EVENTS;
 }
 
+// Mirror com.android.internal.logging.MetricsLogger#action().
+void LogMultiAction(int32_t category, int32_t field, const std::string& value) {
+    android_log_event_list log(kSysuiMultiActionTag);
+    log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
+        << field << value << LOG_ID_EVENTS;
+}
+
 }  // namespace metricslogger
 }  // namespace android
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 377b7dd..089f3b8 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,11 +1,19 @@
 
+cc_library_headers {
+    name: "libnativebridge-dummy-headers",
+
+    host_supported: true,
+    export_include_dirs=["include"],
+}
+
 cc_library {
     name: "libnativebridge",
 
     host_supported: true,
     srcs: ["native_bridge.cc"],
-    shared_libs: ["liblog"],
-    clang: true,
+    shared_libs: ["liblog", "libbase"],
+
+    export_include_dirs=["include"],
 
     cflags: [
         "-Werror",
@@ -14,13 +22,6 @@
     cppflags: [
         "-fvisibility=protected",
     ],
-
-    host_ldlibs: ["-ldl"],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-    },
 }
 
-subdirs = ["tests"]
\ No newline at end of file
+subdirs = ["tests"]
diff --git a/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
similarity index 100%
rename from include/nativebridge/native_bridge.h
rename to libnativebridge/include/nativebridge/native_bridge.h
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 02b4fe7..e24307a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -28,6 +28,7 @@
 
 #include <cstring>
 
+#include <android-base/macros.h>
 #include <log/log.h>
 
 namespace android {
@@ -243,29 +244,12 @@
   }
 }
 
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
-
 bool NeedsNativeBridge(const char* instruction_set) {
   if (instruction_set == nullptr) {
     ALOGE("Null instruction set in NeedsNativeBridge.");
     return false;
   }
-  return strncmp(instruction_set, kRuntimeISA, strlen(kRuntimeISA) + 1) != 0;
+  return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
 }
 
 #ifdef __APPLE__
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index efd3978..9e2e641 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,15 +23,8 @@
         "-Wextra",
         "-Werror",
     ],
+    header_libs: ["libnativebridge-dummy-headers"],
     cppflags: ["-fvisibility=protected"],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-        host: {
-            host_ldlibs: ["-ldl"],
-        },
-    },
 }
 
 cc_library_shared {
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 70b3fcc..b3861e0 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -29,12 +29,12 @@
 
 shared_libraries := \
     liblog \
+    libbase \
     libnativebridge \
     libnativebridge-dummy
 
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_CLANG := true) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -43,7 +43,6 @@
 
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_CLANG := true) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
index 2067ed2..c8ff743 100644
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -16,34 +16,20 @@
 
 #include "NativeBridgeTest.h"
 
+#include <android-base/macros.h>
+
 namespace android {
 
 static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
                                "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
 
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
 TEST_F(NativeBridgeTest, NeedsNativeBridge) {
-    EXPECT_EQ(false, NeedsNativeBridge(kRuntimeISA));
+  EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING));
 
-    const size_t kISACount = sizeof(kISAs)/sizeof(kISAs[0]);
-    for (size_t i = 0; i < kISACount; i++) {
-        EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], kRuntimeISA) != 0,
-                  NeedsNativeBridge(kISAs[i]));
+  const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]);
+  for (size_t i = 0; i < kISACount; i++) {
+    EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0,
+              NeedsNativeBridge(kISAs[i]));
     }
 }
 
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index c1133fb..4b21edc 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -11,15 +11,6 @@
         "libnativebridge",
         "libbase",
     ],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-        host: {
-            host_ldlibs: ["-ldl"],
-        },
-    },
-    clang: true,
     cflags: [
         "-Werror",
         "-Wall",
@@ -28,5 +19,4 @@
         "-fvisibility=hidden",
     ],
     export_include_dirs: ["include"],
-    local_include_dirs: ["include"],
 }
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 36a2e44..7ccd7db 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "nativeloader/native_loader.h"
-#include "ScopedUtfChars.h"
+#include <nativehelper/ScopedUtfChars.h>
 
 #include <dlfcn.h>
 #ifdef __ANDROID__
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 9967ef8..1d43775 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,6 +1,9 @@
 cc_library_shared {
     name: "libnetutils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "dhcpclient.c",
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 70ff528..a9fec7d 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -6,7 +6,6 @@
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 
-    clang: true,
     sanitize: {
         misc_undefined: ["integer"],
     },
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index f28d726..1856e5c 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -555,6 +555,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 /*
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 3007b15..83a9740 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -562,6 +562,7 @@
     } else {
         vprintf(fmt, argp);
     }
+    va_end(argp);
 }
 
 
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index f0c3795..9fa4154 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -33,6 +33,10 @@
 
 int createProcessGroup(uid_t uid, int initialPid);
 
+bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
+bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
+bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
+
 void removeAllProcessGroups(void);
 
 __END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index f5d4e1c..8526b3a 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -35,16 +35,16 @@
 #include <set>
 #include <thread>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
-using namespace std::chrono_literals;
+using android::base::WriteStringToFile;
 
-// Uncomment line below use memory cgroups for keeping track of (forked) PIDs
-// #define USE_MEMCG 1
+using namespace std::chrono_literals;
 
 #define MEM_CGROUP_PATH "/dev/memcg/apps"
 #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
@@ -91,7 +91,6 @@
 };
 
 static const char* getCgroupRootPath() {
-#ifdef USE_MEMCG
     static const char* cgroup_root_path = NULL;
     std::call_once(init_path_flag, [&]() {
             // Check if mem cgroup is mounted, only then check for write-access to avoid
@@ -100,9 +99,6 @@
                     ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
             });
     return cgroup_root_path;
-#else
-    return ACCT_CGROUP_PATH;
-#endif
 }
 
 static int convertUidToPath(char *path, size_t size, uid_t uid)
@@ -409,22 +405,40 @@
 
     strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
 
-    int fd = open(path, O_WRONLY);
-    if (fd == -1) {
-        int ret = -errno;
-        PLOG(ERROR) << "Failed to open " << path;
-        return ret;
-    }
-
-    char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
-    int len = snprintf(pid, sizeof(pid), "%d", initialPid);
-
     int ret = 0;
-    if (write(fd, pid, len) < 0) {
+    if (!WriteStringToFile(std::to_string(initialPid), path)) {
         ret = -errno;
-        PLOG(ERROR) << "Failed to write '" << pid << "' to " << path;
+        PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path;
     }
 
-    close(fd);
     return ret;
 }
+
+static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) {
+    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+    if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) {
+        PLOG(ERROR) << "Memcg is not mounted." << path;
+        return false;
+    }
+
+    convertUidPidToPath(path, sizeof(path), uid, pid);
+    strlcat(path, fileName, sizeof(path));
+
+    if (!WriteStringToFile(std::to_string(value), path)) {
+        PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
+        return false;
+    }
+    return true;
+}
+
+bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
+    return setProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+}
+
+bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
+    return setProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+}
+
+bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
+    return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index aedaa38..b568ee5 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -23,6 +23,9 @@
 cc_library {
     name: "libprocinfo",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     host_supported: true,
     srcs: [
         "process.cpp",
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
index fb140ff..db56fc1 100644
--- a/libprocinfo/include/procinfo/process.h
+++ b/libprocinfo/include/procinfo/process.h
@@ -35,8 +35,18 @@
 
 #if defined(__linux__)
 
+enum ProcessState {
+  kProcessStateUnknown,
+  kProcessStateRunning,
+  kProcessStateSleeping,
+  kProcessStateUninterruptibleWait,
+  kProcessStateStopped,
+  kProcessStateZombie,
+};
+
 struct ProcessInfo {
   std::string name;
+  ProcessState state;
   pid_t tid;
   pid_t pid;
   pid_t ppid;
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index c513e16..6e5be6e 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -44,6 +44,24 @@
   return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
 }
 
+static ProcessState parse_state(const char* state) {
+  switch (*state) {
+    case 'R':
+      return kProcessStateRunning;
+    case 'S':
+      return kProcessStateSleeping;
+    case 'D':
+      return kProcessStateUninterruptibleWait;
+    case 'T':
+      return kProcessStateStopped;
+    case 'Z':
+      return kProcessStateZombie;
+    default:
+      LOG(ERROR) << "unknown process state: " << *state;
+      return kProcessStateUnknown;
+  }
+}
+
 bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
   int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
 
@@ -60,7 +78,7 @@
   }
 
   int field_bitmap = 0;
-  static constexpr int finished_bitmap = 127;
+  static constexpr int finished_bitmap = 255;
   char* line = nullptr;
   size_t len = 0;
 
@@ -98,6 +116,9 @@
     } else if (header == "Gid:") {
       process_info->gid = atoi(tab + 1);
       field_bitmap |= 64;
+    } else if (header == "State:") {
+      process_info->state = parse_state(tab + 1);
+      field_bitmap |= 128;
     }
   }
 
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
index 5ffd236..9da9278 100644
--- a/libprocinfo/process_test.cpp
+++ b/libprocinfo/process_test.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <set>
 #include <thread>
 #include <vector>
@@ -29,6 +30,8 @@
 
 #include <android-base/stringprintf.h>
 
+using namespace std::chrono_literals;
+
 #if !defined(__BIONIC__)
 #include <syscall.h>
 static pid_t gettid() {
@@ -82,3 +85,34 @@
     }
   }).join();
 }
+
+TEST(process_info, process_state) {
+  int pipefd[2];
+  ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+  pid_t forkpid = fork();
+
+  ASSERT_NE(-1, forkpid);
+  if (forkpid == 0) {
+    close(pipefd[1]);
+    char buf;
+    TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1));
+    _exit(0);
+  }
+
+  // Give the child some time to get to the read.
+  std::this_thread::sleep_for(100ms);
+
+  android::procinfo::ProcessInfo procinfo;
+  ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+  ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state);
+
+  ASSERT_EQ(0, kill(forkpid, SIGKILL));
+
+  // Give the kernel some time to kill the child.
+  std::this_thread::sleep_for(100ms);
+
+  ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+  ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state);
+
+  ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
+}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 6ec0991..b894656 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -15,19 +15,11 @@
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
+    shared_libs: [
+        "libz",
+        "libbase",
+    ],
     target: {
-        host: {
-            shared_libs: [
-                "libz-host",
-                "libbase",
-            ],
-        },
-        android: {
-            shared_libs: [
-                "libz",
-                "libbase",
-            ],
-        },
         windows: {
             enabled: true,
         },
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index bd66873..4379635 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -21,10 +21,10 @@
 #include <inttypes.h>
 #include <fcntl.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <string>
 #include <unistd.h>
 
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index 130800e..32f1e1f 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -3,6 +3,9 @@
 cc_library {
     name: "libsuspend",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "autosuspend.c",
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 257d42d..3fae5e6 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -22,10 +22,15 @@
 
 cc_library_shared {
     name: "libsync",
-    vendor_available: true,
     defaults: ["libsync_defaults"],
 }
 
+llndk_library {
+    name: "libsync",
+    symbol_file: "libsync.map.txt",
+    export_include_dirs: ["include"],
+}
+
 // libsync_recovery is only intended for the recovery binary.
 // Future versions of the kernel WILL require an updated libsync, and will break
 // anything statically linked against the current libsync.
@@ -52,5 +57,4 @@
         "-Wno-missing-field-initializers",
         "-Wno-sign-compare",
     ],
-    clang: true,
 }
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index daa28ae..53bb07a 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -17,16 +17,12 @@
 LIBSYNC {
   global:
     sync_merge; # introduced=26
-    sync_get_fence_info; # introduced=26
-    sync_free_fence_info; # introduced=26
+    sync_file_info; # introduced=26
+    sync_file_info_free; # introduced=26
+    sync_wait; # vndk
+    sync_fence_info; # vndk
+    sync_pt_info; # vndk
+    sync_fence_info_free; # vndk
   local:
     *;
 };
-
-LIBSYNC_PLATFORM {
-  global:
-    sync_wait;
-    sync_fence_info;
-    sync_pt_info;
-    sync_fence_info_free;
-} LIBSYNC_PLATFORM;
diff --git a/libsync/sync.c b/libsync/sync.c
index baeccda..6b187fa 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -217,6 +217,8 @@
                   local_info.num_fences * sizeof(struct sync_fence_info));
     if (!info)
         return NULL;
+
+    info->num_fences = local_info.num_fences;
     info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
 
     err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
@@ -275,7 +277,6 @@
     info = calloc(1, sizeof(struct sync_file_info) +
                      num_fences * sizeof(struct sync_fence_info));
     if (!info) {
-        free(legacy_info);
         return NULL;
     }
     info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index f08e97e..0fb86d6 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -448,6 +448,41 @@
     ASSERT_EQ(mergedFence.wait(100), 0);
 }
 
+TEST(FenceTest, GetInfoActive) {
+    SyncTimeline timeline;
+    ASSERT_TRUE(timeline.isValid());
+
+    SyncFence fence(timeline, 1);
+    ASSERT_TRUE(fence.isValid());
+
+    vector<SyncPointInfo> info = fence.getInfo();
+    ASSERT_EQ(info.size(), 1);
+
+    ASSERT_FALSE(info[0].driverName.empty());
+    ASSERT_FALSE(info[0].objectName.empty());
+    ASSERT_EQ(info[0].timeStampNs, 0);
+    ASSERT_EQ(info[0].status, 0);
+}
+
+TEST(FenceTest, GetInfoSignaled) {
+    SyncTimeline timeline;
+    ASSERT_TRUE(timeline.isValid());
+
+    SyncFence fence(timeline, 1);
+    ASSERT_TRUE(fence.isValid());
+
+    ASSERT_EQ(timeline.inc(1), 0);
+    ASSERT_EQ(fence.wait(), 0);
+
+    vector<SyncPointInfo> info = fence.getInfo();
+    ASSERT_EQ(info.size(), 1);
+
+    ASSERT_FALSE(info[0].driverName.empty());
+    ASSERT_FALSE(info[0].objectName.empty());
+    ASSERT_GT(info[0].timeStampNs, 0);
+    ASSERT_EQ(info[0].status, 1);
+}
+
 TEST(StressTest, TwoThreadsSharedTimeline) {
     const int iterations = 1 << 16;
     int counter = 0;
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 550ef42..3a12292 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,6 +1,9 @@
 cc_library_shared {
     name: "libsysutils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "src/SocketListener.cpp",
@@ -20,7 +23,6 @@
         "libbase",
         "libcutils",
         "liblog",
-        "libnl",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libsysutils/include/sysutils/NetlinkEvent.h b/libsysutils/include/sysutils/NetlinkEvent.h
index b80f3ea..f9fc11b 100644
--- a/libsysutils/include/sysutils/NetlinkEvent.h
+++ b/libsysutils/include/sysutils/NetlinkEvent.h
@@ -64,6 +64,7 @@
     bool parseNfPacketMessage(struct nlmsghdr *nh);
     bool parseRtMessage(const struct nlmsghdr *nh);
     bool parseNdUserOptMessage(const struct nlmsghdr *nh);
+    struct nlattr* findNlAttr(const nlmsghdr* nl, size_t hdrlen, uint16_t attr);
 };
 
 #endif
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 1b6076f..87e2684 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -68,7 +68,7 @@
         android_errorWriteLog(0x534e4554, "29831647");
         c->sendMsg(500, "Command too large for buffer", false);
         mSkipToNextNullByte = true;
-        return false;
+        return true;
     }
 
     int offset = 0;
@@ -211,7 +211,6 @@
     return;
 
 overflow:
-    LOG_EVENT_INT(78001, cli->getUid());
     cli->sendMsg(500, "Command too long", false);
     goto out;
 }
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 79bc888..00b1ee2 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "NetlinkEvent"
 
 #include <arpa/inet.h>
+#include <limits.h>
+#include <linux/genetlink.h>
 #include <linux/if.h>
 #include <linux/if_addr.h>
 #include <linux/if_link.h>
@@ -26,12 +28,8 @@
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <net/if.h>
-#include <netinet/in.h>
 #include <netinet/icmp6.h>
-#include <netlink/attr.h>
-#include <netlink/genl/genl.h>
-#include <netlink/handlers.h>
-#include <netlink/msg.h>
+#include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -263,6 +261,18 @@
     return true;
 }
 
+static size_t nlAttrLen(const nlattr* nla) {
+    return nla->nla_len - NLA_HDRLEN;
+}
+
+static const uint8_t* nlAttrData(const nlattr* nla) {
+    return reinterpret_cast<const uint8_t*>(nla) + NLA_HDRLEN;
+}
+
+static uint32_t nlAttrU32(const nlattr* nla) {
+    return *reinterpret_cast<const uint32_t*>(nlAttrData(nla));
+}
+
 /*
  * Parse a LOCAL_NFLOG_PACKET message.
  */
@@ -271,17 +281,17 @@
     int len = 0;
     char* raw = NULL;
 
-    struct nlattr *uid_attr = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_UID);
+    struct nlattr* uid_attr = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_UID);
     if (uid_attr) {
-        uid = ntohl(nla_get_u32(uid_attr));
+        uid = ntohl(nlAttrU32(uid_attr));
     }
 
-    struct nlattr *payload = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
+    struct nlattr* payload = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
     if (payload) {
         /* First 256 bytes is plenty */
-        len = nla_len(payload);
+        len = nlAttrLen(payload);
         if (len > 256) len = 256;
-        raw = (char*) nla_data(payload);
+        raw = (char*)nlAttrData(payload);
     }
 
     char* hex = (char*) calloc(1, 5 + (len * 2));
@@ -646,3 +656,26 @@
     SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
     return NULL;
 }
+
+nlattr* NetlinkEvent::findNlAttr(const nlmsghdr* nh, size_t hdrlen, uint16_t attr) {
+    if (nh == nullptr || NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen) > SSIZE_MAX) {
+        return nullptr;
+    }
+
+    // Skip header, padding, and family header.
+    const ssize_t NLA_START = NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen);
+    ssize_t left = nh->nlmsg_len - NLA_START;
+    uint8_t* hdr = ((uint8_t*)nh) + NLA_START;
+
+    while (left >= NLA_HDRLEN) {
+        nlattr* nla = (nlattr*)hdr;
+        if (nla->nla_type == attr) {
+            return nla;
+        }
+
+        hdr += NLA_ALIGN(nla->nla_len);
+        left -= NLA_ALIGN(nla->nla_len);
+    }
+
+    return nullptr;
+}
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 896dad3..aad0394 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -57,8 +57,6 @@
     count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
             mBuffer, sizeof(mBuffer), require_group, &uid));
     if (count < 0) {
-        if (uid > 0)
-            LOG_EVENT_INT(65537, uid);
         SLOGE("recvmsg failed (%s)", strerror(errno));
         return false;
     }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index eb2b902..f40086e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -29,25 +29,27 @@
         darwin: {
             enabled: false,
         },
-    },
-
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
+        linux_bionic: {
+            enabled: true,
         },
     },
 }
 
-cc_defaults {
-    name: "libunwindstack_common",
+cc_library {
+    name: "libunwindstack",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     defaults: ["libunwindstack_flags"],
+    export_include_dirs: ["include"],
 
     srcs: [
         "ArmExidx.cpp",
         "DwarfCfa.cpp",
+        "DwarfDebugFrame.cpp",
+        "DwarfEhFrame.cpp",
         "DwarfMemory.cpp",
         "DwarfOp.cpp",
         "DwarfSection.cpp",
@@ -55,40 +57,42 @@
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
         "Log.cpp",
-        "Regs.cpp",
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "Regs.cpp",
+        "Unwinder.cpp",
         "Symbols.cpp",
     ],
 
+    target: {
+        // Always disable optimizations for host to make it easier to debug.
+        host: {
+            cflags: ["-O0", "-g"],
+        },
+    },
+
+    arch: {
+        x86: {
+            srcs: ["AsmGetRegsX86.S"],
+        },
+        x86_64: {
+            srcs: ["AsmGetRegsX86_64.S"],
+        },
+    },
+
     shared_libs: [
         "libbase",
         "liblog",
-    ],
-}
-
-cc_library {
-    name: "libunwindstack",
-    defaults: ["libunwindstack_common"],
-}
-
-cc_library {
-    name: "libunwindstack_debug",
-    defaults: ["libunwindstack_common"],
-
-    cflags: [
-        "-UNDEBUG",
-        "-O0",
-        "-g",
+        "liblzma",
     ],
 }
 
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
-cc_defaults {
-    name: "libunwindstack_test_common",
+cc_test {
+    name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
 
     srcs: [
@@ -96,24 +100,33 @@
         "tests/ArmExidxExtractTest.cpp",
         "tests/DwarfCfaLogTest.cpp",
         "tests/DwarfCfaTest.cpp",
+        "tests/DwarfDebugFrameTest.cpp",
+        "tests/DwarfEhFrameTest.cpp",
         "tests/DwarfMemoryTest.cpp",
         "tests/DwarfOpLogTest.cpp",
         "tests/DwarfOpTest.cpp",
         "tests/DwarfSectionTest.cpp",
         "tests/DwarfSectionImplTest.cpp",
+        "tests/ElfFake.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
+        "tests/ElfTestUtils.cpp",
         "tests/LogFake.cpp",
-        "tests/MapInfoTest.cpp",
+        "tests/MapInfoGetElfTest.cpp",
         "tests/MapsTest.cpp",
+        "tests/MemoryBufferTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
+        "tests/MemoryTest.cpp",
+        "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
+        "tests/UnwindTest.cpp",
+        "tests/UnwinderTest.cpp",
     ],
 
     cflags: [
@@ -124,65 +137,68 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "liblzma",
+        "libunwindstack",
     ],
 
     static_libs: [
         "libgmock",
     ],
 
-    target: {
-        linux: {
-            host_ldlibs: [
-                "-lrt",
-            ],
-        },
-    },
-}
-
-// These unit tests run against the shared library.
-cc_test {
-    name: "libunwindstack_test",
-    defaults: ["libunwindstack_test_common"],
-
-    shared_libs: [
-        "libunwindstack",
-    ],
-}
-
-// These unit tests run against the static debug library.
-cc_test {
-    name: "libunwindstack_test_debug",
-    defaults: ["libunwindstack_test_common"],
-
-    static_libs: [
-        "libunwindstack_debug",
+    data: [
+        "tests/files/elf32.xz",
+        "tests/files/elf64.xz",
     ],
 }
 
 //-------------------------------------------------------------------------
-// Utility Executables
+// Tools
 //-------------------------------------------------------------------------
 cc_defaults {
-    name: "libunwindstack_executables",
+    name: "libunwindstack_tools",
     defaults: ["libunwindstack_flags"],
 
     shared_libs: [
         "libunwindstack",
         "libbase",
+        "liblzma",
     ],
+}
 
-    static_libs: [
-        "liblog",
+cc_binary {
+    name: "unwind",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind.cpp",
     ],
-
-    compile_multilib: "both",
 }
 
 cc_binary {
     name: "unwind_info",
-    defaults: ["libunwindstack_executables"],
+    defaults: ["libunwindstack_tools"],
 
     srcs: [
-        "unwind_info.cpp",
+        "tools/unwind_info.cpp",
+    ],
+}
+
+cc_binary {
+    name: "unwind_symbols",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind_symbols.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 {
+    name: "gen_gnudebugdata",
+    defaults: ["libunwindstack_flags"],
+
+    srcs: [
+        "tests/GenGnuDebugdata.cpp",
     ],
 }
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 12adf57..fed3e0e 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <stdint.h>
 
 #include <deque>
@@ -22,11 +21,15 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "ArmExidx.h"
-#include "Log.h"
+#include "Check.h"
 #include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
 
 void ArmExidx::LogRawData() {
   std::string log_str("Raw Data:");
@@ -173,7 +176,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
-  assert((byte >> 4) == 0x8);
+  CHECK((byte >> 4) == 0x8);
 
   uint16_t registers = (byte & 0xf) << 8;
   if (!GetByte(&byte)) {
@@ -232,7 +235,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
-  assert((byte >> 4) == 0x9);
+  CHECK((byte >> 4) == 0x9);
 
   uint8_t bits = byte & 0xf;
   if (bits == 13 || bits == 15) {
@@ -258,7 +261,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
-  assert((byte >> 4) == 0xa);
+  CHECK((byte >> 4) == 0xa);
 
   // 10100nnn: Pop r4-r[4+nnn]
   // 10101nnn: Pop r4-r[4+nnn], r14
@@ -419,7 +422,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
-  assert((byte & ~0x07) == 0xb8);
+  CHECK((byte & ~0x07) == 0xb8);
 
   // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
   if (log_) {
@@ -439,7 +442,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
-  assert((byte >> 6) == 0x2);
+  CHECK((byte >> 6) == 0x2);
 
   switch ((byte >> 4) & 0x3) {
   case 0:
@@ -469,7 +472,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
-  assert((byte & ~0x07) == 0xc0);
+  CHECK((byte & ~0x07) == 0xc0);
 
   uint8_t bits = byte & 0x7;
   if (bits == 6) {
@@ -550,7 +553,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
-  assert((byte & ~0x07) == 0xc8);
+  CHECK((byte & ~0x07) == 0xc8);
 
   uint8_t bits = byte & 0x7;
   if (bits == 0) {
@@ -605,7 +608,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
-  assert((byte & ~0x07) == 0xd0);
+  CHECK((byte & ~0x07) == 0xd0);
 
   // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
   if (log_) {
@@ -624,7 +627,7 @@
 }
 
 inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
-  assert((byte >> 6) == 0x3);
+  CHECK((byte >> 6) == 0x3);
 
   switch ((byte >> 3) & 0x7) {
   case 0:
@@ -684,3 +687,5 @@
   while (Decode());
   return status_ == ARM_STATUS_FINISH;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 8c7f15a..f4361d4 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -21,6 +21,8 @@
 
 #include <deque>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Memory;
 class RegsArm;
@@ -105,4 +107,6 @@
   bool pc_set_ = false;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S
new file mode 100644
index 0000000..14927a3
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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
+  .global AsmGetRegs
+  .balign 16
+  .type AsmGetRegs, @function
+AsmGetRegs:
+  .cfi_startproc
+  mov 4(%esp), %eax
+  movl $0, (%eax)
+  movl %ecx, 4(%eax)
+  movl %edx, 8(%eax)
+  movl %ebx, 12(%eax)
+
+  /* ESP */
+  leal 4(%esp), %ecx
+  movl %ecx, 16(%eax)
+
+  movl %ebp, 20(%eax)
+  movl %esi, 24(%eax)
+  movl %edi, 28(%eax)
+
+  /* EIP */
+  movl (%esp), %ecx
+  movl %ecx, 32(%eax)
+
+  movl %cs, 36(%eax)
+  movl %ss, 40(%eax)
+  movl %ds, 44(%eax)
+  movl %es, 48(%eax)
+  movl %fs, 52(%eax)
+  movl %gs, 56(%eax)
+  ret
+
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S
new file mode 100644
index 0000000..4cd3b6f
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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
+  .global AsmGetRegs
+  .balign 16
+  .type AsmGetRegs, @function
+AsmGetRegs:
+  .cfi_startproc
+  movq %rax, (%rdi)
+  movq %rdx, 8(%rdi)
+  movq %rcx, 16(%rdi)
+  movq %rbx, 24(%rdi)
+  movq %rsi, 32(%rdi)
+  movq %rdi, 40(%rdi)
+  movq %rbp, 48(%rdi)
+
+  /* RSP */
+  lea 8(%rsp), %rax
+  movq %rax, 56(%rdi)
+
+  movq %r8, 64(%rdi)
+  movq %r9, 72(%rdi)
+  movq %r10, 80(%rdi)
+  movq %r11, 88(%rdi)
+  movq %r12, 96(%rdi)
+  movq %r13, 104(%rdi)
+  movq %r14, 112(%rdi)
+  movq %r15, 120(%rdi)
+
+  /* RIP */
+  movq (%rsp), %rax
+  movq %rax, 128(%rdi)
+  ret
+
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
new file mode 100644
index 0000000..2d216d7
--- /dev/null
+++ b/libunwindstack/Check.h
@@ -0,0 +1,34 @@
+/*
+ * 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_ERROR_H
+#define _LIBUNWINDSTACK_ERROR_H
+
+#include <stdlib.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+#define CHECK(assertion)                                   \
+  if (__builtin_expect(!(assertion), false)) {             \
+    log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+    abort();                                               \
+  }
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 006f039..b1d55ba 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -23,12 +23,15 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
 #include "DwarfEncoding.h"
-#include "DwarfMemory.h"
+#include "DwarfError.h"
 #include "DwarfOp.h"
-#include "DwarfStructs.h"
-#include "Log.h"
+
+namespace unwindstack {
 
 template <typename AddressType>
 constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
@@ -711,3 +714,5 @@
 // Explicitly instantiate DwarfCfa.
 template class DwarfCfa<uint32_t>;
 template class DwarfCfa<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 42ebae1..62b9b7a 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -24,10 +24,13 @@
 #include <type_traits>
 #include <vector>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
 #include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
+
+namespace unwindstack {
 
 // DWARF Standard home: http://dwarfstd.org/
 // This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
@@ -252,4 +255,6 @@
   };
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..5707596
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.cpp
@@ -0,0 +1,316 @@
+/*
+ * 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 <stdlib.h>
+
+#include <algorithm>
+
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+  offset_ = offset;
+  end_offset_ = offset + size;
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+
+  return CreateSortedFdeList();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
+  uint8_t version;
+  if (!memory_.ReadBytes(&version, 1)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  // Read the augmentation string.
+  std::vector<char> aug_string;
+  char aug_value;
+  bool get_encoding = false;
+  do {
+    if (!memory_.ReadBytes(&aug_value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    if (aug_value == 'R') {
+      get_encoding = true;
+    }
+    aug_string.push_back(aug_value);
+  } while (aug_value != '\0');
+
+  if (version == 4) {
+    // Skip the Address Size field.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+    // Read the segment size.
+    if (!memory_.ReadBytes(segment_size, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } else {
+    *segment_size = 0;
+  }
+
+  if (aug_string[0] != 'z' || !get_encoding) {
+    // No encoding
+    return true;
+  }
+
+  // Skip code alignment factor
+  uint8_t value;
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } while (value & 0x80);
+
+  // Skip data alignment factor
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } while (value & 0x80);
+
+  if (version == 1) {
+    // Skip return address register.
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+  } else {
+    // Skip return address register.
+    do {
+      if (!memory_.ReadBytes(&value, 1)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+    } while (value & 0x80);
+  }
+
+  // Skip the augmentation length.
+  do {
+    if (!memory_.ReadBytes(&value, 1)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+  } while (value & 0x80);
+
+  for (size_t i = 1; i < aug_string.size(); i++) {
+    if (aug_string[i] == 'R') {
+      if (!memory_.ReadBytes(encoding, 1)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+      // Got the encoding, that's all we are looking for.
+      return true;
+    } else if (aug_string[i] == 'L') {
+      memory_.set_cur_offset(memory_.cur_offset() + 1);
+    } else if (aug_string[i] == 'P') {
+      uint8_t encoding;
+      if (!memory_.ReadBytes(&encoding, 1)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+      uint64_t value;
+      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+    }
+  }
+
+  // It should be impossible to get here.
+  abort();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
+                                              uint8_t encoding) {
+  if (segment_size != 0) {
+    memory_.set_cur_offset(memory_.cur_offset() + 1);
+  }
+
+  uint64_t start;
+  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  uint64_t length;
+  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+  if (length != 0) {
+    fdes_.emplace_back(entry_offset, start, length);
+  }
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
+  memory_.set_cur_offset(offset_);
+
+  // Loop through all of the entries and read just enough to create
+  // a sorted list of pcs.
+  // This code assumes that first comes the cie, then the fdes that
+  // it applies to.
+  uint64_t cie_offset = 0;
+  uint8_t address_encoding;
+  uint8_t segment_size;
+  while (memory_.cur_offset() < end_offset_) {
+    uint64_t cur_entry_offset = memory_.cur_offset();
+
+    // Figure out the entry length and type.
+    uint32_t value32;
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    uint64_t next_entry_offset;
+    if (value32 == static_cast<uint32_t>(-1)) {
+      uint64_t value64;
+      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+      next_entry_offset = memory_.cur_offset() + value64;
+
+      // Read the Cie Id of a Cie or the pointer of the Fde.
+      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+
+      if (value64 == static_cast<uint64_t>(-1)) {
+        // Cie 64 bit
+        address_encoding = DW_EH_PE_sdata8;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        if (offset_ + value64 != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+
+        // Fde 64 bit
+        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+          return false;
+        }
+      }
+    } else {
+      next_entry_offset = memory_.cur_offset() + value32;
+
+      // Read the Cie Id of a Cie or the pointer of the Fde.
+      if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+        last_error_ = DWARF_ERROR_MEMORY_INVALID;
+        return false;
+      }
+
+      if (value32 == static_cast<uint32_t>(-1)) {
+        // Cie 32 bit
+        address_encoding = DW_EH_PE_sdata4;
+        if (!GetCieInfo(&segment_size, &address_encoding)) {
+          return false;
+        }
+        cie_offset = cur_entry_offset;
+      } else {
+        if (offset_ + value32 != cie_offset) {
+          // This means that this Fde is not following the Cie.
+          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          return false;
+        }
+
+        // Fde 32 bit
+        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
+          return false;
+        }
+      }
+    }
+
+    if (next_entry_offset < memory_.cur_offset()) {
+      // This indicates some kind of corruption, or malformed section data.
+      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+    }
+    memory_.set_cur_offset(next_entry_offset);
+  }
+
+  // Sort the entries.
+  std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
+    if (a.start == b.start) return a.end < b.end;
+    return a.start < b.start;
+  });
+
+  fde_count_ = fdes_.size();
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
+
+  size_t first = 0;
+  size_t last = fde_count_;
+  while (first < last) {
+    size_t current = (first + last) / 2;
+    const FdeInfo* info = &fdes_[current];
+    if (pc >= info->start && pc <= info->end) {
+      *fde_offset = info->offset;
+      return true;
+    }
+
+    if (pc < info->start) {
+      last = current;
+    } else {
+      first = current + 1;
+    }
+  }
+  return false;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
+  if (index >= fdes_.size()) {
+    return nullptr;
+  }
+  return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
+// Explicitly instantiate DwarfDebugFrame.
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 0000000..6a6178e
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,80 @@
+/*
+ * 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_DWARF_DEBUG_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::fde_count_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+
+  struct FdeInfo {
+    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
+        : offset(offset), start(start), end(start + length) {}
+
+    uint64_t offset;
+    AddressType start;
+    AddressType end;
+  };
+
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfDebugFrame() = default;
+
+  bool Init(uint64_t offset, uint64_t size) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+  const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+  bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }
+
+  bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }
+
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }
+
+  uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
+
+  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
+
+  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
+
+  bool CreateSortedFdeList();
+
+ protected:
+  uint64_t offset_;
+  uint64_t end_offset_;
+
+  std::vector<FdeInfo> fdes_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
new file mode 100644
index 0000000..db8f558
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEhFrame.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
+  uint8_t data[4];
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+
+  // Read the first four bytes all at once.
+  if (!memory_.ReadBytes(data, 4)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  version_ = data[0];
+  if (version_ != 1) {
+    // Unknown version.
+    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  ptr_encoding_ = data[1];
+  uint8_t fde_count_encoding = data[2];
+  table_encoding_ = data[3];
+  table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    return false;
+  }
+
+  entries_offset_ = memory_.cur_offset();
+  entries_end_ = offset + size;
+  entries_data_offset_ = offset;
+  cur_entries_offset_ = entries_offset_;
+
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
+  const FdeInfo* info = GetFdeInfoFromIndex(index);
+  if (info == nullptr) {
+    return nullptr;
+  }
+  return this->GetFdeFromOffset(info->offset);
+}
+
+template <typename AddressType>
+const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
+    size_t index) {
+  auto entry = fde_info_.find(index);
+  if (entry != fde_info_.end()) {
+    return &fde_info_[index];
+  }
+  FdeInfo* info = &fde_info_[index];
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+  memory_.set_pc_offset(memory_.cur_offset());
+  uint64_t value;
+  if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+      !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    fde_info_.erase(index);
+    return nullptr;
+  }
+  info->pc = value + 4;
+  return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+                                                   uint64_t total_entries) {
+  CHECK(fde_count_ > 0);
+  CHECK(total_entries <= fde_count_);
+
+  size_t first = 0;
+  size_t last = total_entries;
+  while (first < last) {
+    size_t current = (first + last) / 2;
+    const FdeInfo* info = GetFdeInfoFromIndex(current);
+    if (pc == info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      last = current;
+    } else {
+      first = current + 1;
+    }
+  }
+  if (last != 0) {
+    const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+    *fde_offset = info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+  CHECK(fde_count_ != 0);
+  last_error_ = DWARF_ERROR_NONE;
+  // We can do a binary search if the pc is in the range of the elements
+  // that have already been cached.
+  if (!fde_info_.empty()) {
+    const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
+    if (pc >= info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+    }
+  }
+
+  if (cur_entries_offset_ == 0) {
+    // All entries read, or error encountered.
+    return false;
+  }
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(cur_entries_offset_);
+  cur_entries_offset_ = 0;
+
+  FdeInfo* prev_info = nullptr;
+  for (size_t current = fde_info_.size();
+       current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
+    memory_.set_pc_offset(memory_.cur_offset());
+    uint64_t value;
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+
+    FdeInfo* info = &fde_info_[current];
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+      fde_info_.erase(current);
+      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      return false;
+    }
+    info->pc = value + 4;
+
+    if (pc < info->pc) {
+      if (prev_info == nullptr) {
+        return false;
+      }
+      cur_entries_offset_ = memory_.cur_offset();
+      *fde_offset = prev_info->offset;
+      return true;
+    }
+    prev_info = info;
+  }
+
+  if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
+    *fde_offset = prev_info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
+
+  if (table_entry_size_ > 0) {
+    // Do a binary search since the size of each table entry is fixed.
+    return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
+  } else {
+    // Do a sequential search since each table entry size is variable.
+    return GetFdeOffsetSequential(pc, fde_offset);
+  }
+}
+
+// Explicitly instantiate DwarfEhFrame.
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 0000000..4207b42
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::fde_count_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+
+  struct FdeInfo {
+    AddressType pc;
+    uint64_t offset;
+  };
+
+  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfEhFrame() = default;
+
+  bool Init(uint64_t offset, uint64_t size) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
+
+  const DwarfFde* GetFdeFromIndex(size_t index) override;
+
+  bool IsCie32(uint32_t value32) override { return value32 == 0; }
+
+  bool IsCie64(uint64_t value64) override { return value64 == 0; }
+
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return memory_.cur_offset() - pointer - 4;
+  }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return memory_.cur_offset() - pointer - 8;
+  }
+
+  uint64_t AdjustPcFromFde(uint64_t pc) override {
+    // The eh_frame uses relative pcs.
+    return pc + memory_.cur_offset();
+  }
+
+  const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
+
+  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
+
+ protected:
+  uint8_t version_;
+  uint8_t ptr_encoding_;
+  uint8_t table_encoding_;
+  size_t table_entry_size_;
+
+  uint64_t ptr_offset_;
+
+  uint64_t entries_offset_;
+  uint64_t entries_end_;
+  uint64_t entries_data_offset_;
+  uint64_t cur_entries_offset_ = 0;
+
+  std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
index 0ff3b8c..20db222 100644
--- a/libunwindstack/DwarfEncoding.h
+++ b/libunwindstack/DwarfEncoding.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 enum DwarfEncoding : uint8_t {
   DW_EH_PE_omit = 0xff,
 
@@ -44,4 +46,6 @@
   DW_EH_PE_block = 0x0f,
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
index c00f17d..54199b8 100644
--- a/libunwindstack/DwarfError.h
+++ b/libunwindstack/DwarfError.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 enum DwarfError : uint8_t {
   DWARF_ERROR_NONE,
   DWARF_ERROR_MEMORY_INVALID,
@@ -31,4 +33,6 @@
   DWARF_ERROR_UNSUPPORTED_VERSION,
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index 11806ea..901f492 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <stdint.h>
 
 #include <string>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
 #include "DwarfEncoding.h"
-#include "DwarfMemory.h"
-#include "Memory.h"
+
+namespace unwindstack {
 
 bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
   if (!memory_->Read(cur_offset_, dst, num_bytes)) {
@@ -100,8 +103,8 @@
 }
 
 bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
-  assert((encoding & 0x0f) == 0);
-  assert(encoding != DW_EH_PE_aligned);
+  CHECK((encoding & 0x0f) == 0);
+  CHECK(encoding != DW_EH_PE_aligned);
 
   // Handle the encoding.
   switch (encoding) {
@@ -232,7 +235,7 @@
       return false;
   }
 
-  return AdjustEncodedValue(encoding & 0xf0, value);
+  return AdjustEncodedValue(encoding & 0x70, value);
 }
 
 // Instantiate all of the needed template functions.
@@ -246,3 +249,5 @@
 
 template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
 template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 507ca08..b3fd0df 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -22,12 +22,15 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfError.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "Log.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
 
 template <typename AddressType>
 constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
@@ -460,3 +463,5 @@
 // Explicitly instantiate DwarfOp.
 template class DwarfOp<uint32_t>;
 template class DwarfOp<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index 0f4b36d..c29bf35 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -25,6 +25,9 @@
 #include <vector>
 
 #include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+namespace unwindstack {
 
 enum DwarfVersion : uint8_t {
   DWARF_VERSION_2 = 2,
@@ -1633,4 +1636,6 @@
   };
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 4c98aa3..8b30b76 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -16,16 +16,22 @@
 
 #include <stdint.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfCfa.h"
+#include "DwarfEncoding.h"
 #include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "DwarfSection.h"
-#include "DwarfStructs.h"
-#include "Log.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
+
+DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
 
 const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
   uint64_t fde_offset;
@@ -41,7 +47,8 @@
   return nullptr;
 }
 
-bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+  last_error_ = DWARF_ERROR_NONE;
   const DwarfFde* fde = GetFdeFromPc(pc);
   if (fde == nullptr || fde->cie == nullptr) {
     last_error_ = DWARF_ERROR_ILLEGAL_STATE;
@@ -55,7 +62,7 @@
   }
 
   // Now eval the actual registers.
-  return Eval(fde->cie, process_memory, loc_regs, regs);
+  return Eval(fde->cie, process_memory, loc_regs, regs, finished);
 }
 
 template <typename AddressType>
@@ -85,7 +92,8 @@
 
 template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
-                                         const dwarf_loc_regs_t& loc_regs, Regs* regs) {
+                                         const dwarf_loc_regs_t& loc_regs, Regs* regs,
+                                         bool* finished) {
   RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
   if (cie->return_address_register >= cur_regs->total_regs()) {
     last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
@@ -217,12 +225,14 @@
   // Find the return address location.
   if (return_address_undefined) {
     cur_regs->set_pc(0);
+    *finished = true;
   } else {
     cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+    *finished = false;
   }
   cur_regs->set_sp(cfa);
-  // Stop if the cfa and pc are the same.
-  return prev_cfa != cfa || prev_pc != cur_regs->pc();
+  // Return false if the unwind is not finished or the cfa and pc didn't change.
+  return *finished || prev_cfa != cfa || prev_pc != cur_regs->pc();
 }
 
 template <typename AddressType>
@@ -248,6 +258,9 @@
     last_error_ = DWARF_ERROR_MEMORY_INVALID;
     return false;
   }
+  // Set the default for the lsda encoding.
+  cie->lsda_encoding = DW_EH_PE_omit;
+
   if (length32 == static_cast<uint32_t>(-1)) {
     // 64 bit Cie
     uint64_t length64;
@@ -541,3 +554,5 @@
 // Explicitly instantiate DwarfSectionImpl
 template class DwarfSectionImpl<uint32_t>;
 template class DwarfSectionImpl<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 272b5f0..dc6591d 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -23,12 +23,17 @@
 #define LOG_TAG "unwind"
 #include <log/log.h>
 
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "ElfInterfaceArm.h"
 #include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+#include "Symbols.h"
+
+namespace unwindstack {
 
 bool Elf::Init() {
   if (!memory_) {
@@ -49,6 +54,65 @@
   return valid_;
 }
 
+// It is expensive to initialize the .gnu_debugdata section. Provide a method
+// to initialize this data separately.
+void Elf::InitGnuDebugdata() {
+  if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
+    return;
+  }
+
+  gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory());
+  gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
+  ElfInterface* gnu = gnu_debugdata_interface_.get();
+  if (gnu == nullptr) {
+    return;
+  }
+  if (gnu->Init()) {
+    gnu->InitHeaders();
+  } else {
+    // Free all of the memory associated with the gnu_debugdata section.
+    gnu_debugdata_memory_.reset(nullptr);
+    gnu_debugdata_interface_.reset(nullptr);
+  }
+}
+
+bool Elf::GetSoname(std::string* name) {
+  return valid_ && interface_->GetSoname(name);
+}
+
+uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
+  uint64_t load_bias = 0;
+  if (valid()) {
+    load_bias = interface_->load_bias();
+  }
+
+  return pc - map_info->start + load_bias + map_info->elf_offset;
+}
+
+bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+  return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+                    (gnu_debugdata_interface_ &&
+                     gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
+}
+
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
+  if (!valid_) {
+    return false;
+  }
+  if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
+    *finished = false;
+    return true;
+  }
+  return interface_->Step(rel_pc, regs, process_memory, finished) ||
+         (gnu_debugdata_interface_ &&
+          gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
+}
+
+uint64_t Elf::GetLoadBias() {
+  if (!valid_) return 0;
+  return interface_->load_bias();
+}
+
 bool Elf::IsValidElf(Memory* memory) {
   if (memory == nullptr) {
     return false;
@@ -66,6 +130,28 @@
   return true;
 }
 
+void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+  if (!IsValidElf(memory)) {
+    *valid = false;
+    return;
+  }
+  *size = 0;
+  *valid = true;
+
+  // Now read the section header information.
+  uint8_t class_type;
+  if (!memory->Read(EI_CLASS, &class_type, 1)) {
+    return;
+  }
+  if (class_type == ELFCLASS32) {
+    ElfInterface32::GetMaxSize(memory, size);
+  } else if (class_type == ELFCLASS64) {
+    ElfInterface64::GetMaxSize(memory, size);
+  } else {
+    *valid = false;
+  }
+}
+
 ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
   if (!IsValidElf(memory)) {
     return nullptr;
@@ -90,24 +176,27 @@
     machine_type_ = e_machine;
     if (e_machine == EM_ARM) {
       interface.reset(new ElfInterfaceArm(memory));
-    } else {
+    } else if (e_machine == EM_386) {
       interface.reset(new ElfInterface32(memory));
+    } else {
+      ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
+      return nullptr;
     }
   } else if (class_type_ == ELFCLASS64) {
     Elf64_Half e_machine;
     if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
       return nullptr;
     }
-
     if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) {
       // Unsupported.
       ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
       return nullptr;
     }
-
     machine_type_ = e_machine;
     interface.reset(new ElfInterface64(memory));
   }
 
   return interface.release();
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 087457c..46a3f3f 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -20,9 +20,102 @@
 #include <memory>
 #include <string>
 
-#include "ElfInterface.h"
-#include "Memory.h"
-#include "Regs.h"
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+ElfInterface::~ElfInterface() {
+  for (auto symbol : symbols_) {
+    delete symbol;
+  }
+}
+
+Memory* ElfInterface::CreateGnuDebugdataMemory() {
+  if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
+    return nullptr;
+  }
+
+  // TODO: Only call these initialization functions once.
+  CrcGenerateTable();
+  Crc64GenerateTable();
+
+  std::vector<uint8_t> src(gnu_debugdata_size_);
+  if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
+    gnu_debugdata_offset_ = 0;
+    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+    return nullptr;
+  }
+
+  ISzAlloc alloc;
+  CXzUnpacker state;
+  alloc.Alloc = [](void*, size_t size) { return malloc(size); };
+  alloc.Free = [](void*, void* ptr) { return free(ptr); };
+
+  XzUnpacker_Construct(&state, &alloc);
+
+  std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
+  int return_val;
+  size_t src_offset = 0;
+  size_t dst_offset = 0;
+  ECoderStatus status;
+  dst->Resize(5 * gnu_debugdata_size_);
+  do {
+    size_t src_remaining = src.size() - src_offset;
+    size_t dst_remaining = dst->Size() - dst_offset;
+    if (dst_remaining < 2 * gnu_debugdata_size_) {
+      dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
+      dst_remaining += 2 * gnu_debugdata_size_;
+    }
+    return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
+                                 &src_remaining, CODER_FINISH_ANY, &status);
+    src_offset += src_remaining;
+    dst_offset += dst_remaining;
+  } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
+  XzUnpacker_Free(&state);
+  if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
+    gnu_debugdata_offset_ = 0;
+    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+    return nullptr;
+  }
+
+  // Shrink back down to the exact size.
+  dst->Resize(dst_offset);
+
+  return dst.release();
+}
+
+template <typename AddressType>
+void ElfInterface::InitHeadersWithTemplate() {
+  if (eh_frame_offset_ != 0) {
+    eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+      eh_frame_.reset(nullptr);
+      eh_frame_offset_ = 0;
+      eh_frame_size_ = static_cast<uint64_t>(-1);
+    }
+  }
+
+  if (debug_frame_offset_ != 0) {
+    debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
+    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+      debug_frame_.reset(nullptr);
+      debug_frame_offset_ = 0;
+      debug_frame_size_ = static_cast<uint64_t>(-1);
+    }
+  }
+}
 
 template <typename EhdrType, typename PhdrType, typename ShdrType>
 bool ElfInterface::ReadAllHeaders() {
@@ -34,7 +127,13 @@
   if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
     return false;
   }
-  return ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
+
+  // We could still potentially unwind without the section header
+  // information, so ignore any errors.
+  if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) {
+    log(0, "Malformed section header found, ignoring...");
+  }
+  return true;
 }
 
 template <typename EhdrType, typename PhdrType>
@@ -124,12 +223,39 @@
   }
 
   // Skip the first header, it's always going to be NULL.
+  offset += ehdr.e_shentsize;
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
     if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
       return false;
     }
 
-    if (shdr.sh_type == SHT_PROGBITS) {
+    if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
+      if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+        return false;
+      }
+      // Need to go get the information about the section that contains
+      // the string terminated names.
+      ShdrType str_shdr;
+      if (shdr.sh_link >= ehdr.e_shnum) {
+        return false;
+      }
+      uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
+      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+        return false;
+      }
+      if (str_shdr.sh_type != SHT_STRTAB) {
+        return false;
+      }
+      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
+                              sizeof(str_shdr.sh_offset))) {
+        return false;
+      }
+      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
+        return false;
+      }
+      symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
+                                     str_shdr.sh_offset, str_shdr.sh_size));
+    } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
       // Look for the .debug_frame and .gnu_debugdata.
       if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
         return false;
@@ -137,18 +263,20 @@
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+          uint64_t* offset_ptr = nullptr;
+          uint64_t* size_ptr = nullptr;
           if (name == ".debug_frame") {
-            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
-              debug_frame_offset_ = shdr.sh_offset;
-              debug_frame_size_ = shdr.sh_size;
-            }
+            offset_ptr = &debug_frame_offset_;
+            size_ptr = &debug_frame_size_;
           } else if (name == ".gnu_debugdata") {
-            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
-              gnu_debugdata_offset_ = shdr.sh_offset;
-              gnu_debugdata_size_ = shdr.sh_size;
-            }
+            offset_ptr = &gnu_debugdata_offset_;
+            size_ptr = &gnu_debugdata_size_;
+          }
+          if (offset_ptr != nullptr &&
+              memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+              memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+            *offset_ptr = shdr.sh_offset;
+            *size_ptr = shdr.sh_size;
           }
         }
       }
@@ -205,11 +333,62 @@
   return true;
 }
 
-bool ElfInterface::Step(uint64_t, Regs*, Memory*) {
+template <typename SymType>
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
+                                               uint64_t* func_offset) {
+  if (symbols_.empty()) {
+    return false;
+  }
+
+  for (const auto symbol : symbols_) {
+    if (symbol->GetName<SymType>(addr, load_bias_, memory_, name, func_offset)) {
+      return true;
+    }
+  }
   return false;
 }
 
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+  // Need to subtract off the load_bias to get the correct pc.
+  if (pc < load_bias_) {
+    return false;
+  }
+  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)) {
+    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)) {
+    return true;
+  }
+  return false;
+}
+
+// This is an estimation of the size of the elf file using the location
+// of the section headers and size. This assumes that the section headers
+// are at the end of the elf file. If the elf has a load bias, the size
+// will be too large, but this is acceptable.
+template <typename EhdrType>
+void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
+  EhdrType ehdr;
+  if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+    return;
+  }
+  if (ehdr.e_shnum == 0) {
+    return;
+  }
+  *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
+}
+
 // Instantiate all of the needed template functions.
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+
 template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
 template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
 
@@ -221,3 +400,13 @@
 
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
+                                                                   uint64_t*);
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
+                                                                   uint64_t*);
+
+template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index bab84cc..17364d0 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -17,12 +17,14 @@
 #include <elf.h>
 #include <stdint.h>
 
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "ArmExidx.h"
-#include "ElfInterface.h"
 #include "ElfInterfaceArm.h"
 #include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
 
 bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
   if (start_offset_ == 0 || total_entries_ == 0) {
@@ -97,22 +99,25 @@
   return true;
 }
 
-bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool ElfInterfaceArm::Step(uint64_t pc, 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) || StepExidx(pc, regs, process_memory);
+  return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+         StepExidx(pc, regs, process_memory, finished);
 }
 
-bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
   uint64_t entry_offset;
   if (!FindEntry(pc, &entry_offset)) {
     return false;
   }
+
   ArmExidx arm(regs_arm, memory_, process_memory);
   arm.set_cfa(regs_arm->sp());
+  bool return_value = false;
   if (arm.ExtractEntryData(entry_offset) && arm.Eval()) {
     // If the pc was not set, then use the LR registers for the PC.
     if (!arm.pc_set()) {
@@ -123,7 +128,15 @@
     }
     regs_arm->set_sp(arm.cfa());
     (*regs_arm)[ARM_REG_SP] = regs_arm->sp();
+    *finished = false;
+    return_value = true;
+  }
+
+  if (arm.status() == ARM_STATUS_NO_UNWIND) {
+    *finished = true;
     return true;
   }
-  return false;
+  return return_value;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index ece694f..bfe7704 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -23,8 +23,10 @@
 #include <iterator>
 #include <unordered_map>
 
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class ElfInterfaceArm : public ElfInterface32 {
  public:
@@ -68,9 +70,9 @@
 
   bool HandleType(uint64_t offset, uint32_t type) override;
 
-  bool Step(uint64_t pc, Regs* regs, Memory* process_memory) override;
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
-  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory);
+  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
 
   uint64_t start_offset() { return start_offset_; }
 
@@ -87,4 +89,6 @@
   std::unordered_map<size_t, uint32_t> addrs_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
index faeb66c..436e23c 100644
--- a/libunwindstack/Log.cpp
+++ b/libunwindstack/Log.cpp
@@ -25,7 +25,9 @@
 
 #include <android-base/stringprintf.h>
 
-#include "Log.h"
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
 
 static bool g_print_to_stdout = false;
 
@@ -51,3 +53,5 @@
   }
   va_end(args);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
index 323ce80..1fb9309 100644
--- a/libunwindstack/Machine.h
+++ b/libunwindstack/Machine.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 enum ArmReg : uint16_t {
   ARM_REG_R0 = 0,
   ARM_REG_R1,
@@ -83,51 +85,57 @@
   ARM64_REG_LR = ARM64_REG_R30,
 };
 
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
 enum X86Reg : uint16_t {
   X86_REG_EAX = 0,
-  X86_REG_ECX,
-  X86_REG_EDX,
-  X86_REG_EBX,
-  X86_REG_ESP,
-  X86_REG_EBP,
-  X86_REG_ESI,
-  X86_REG_EDI,
-  X86_REG_EIP,
-  X86_REG_EFL,
-  X86_REG_CS,
-  X86_REG_SS,
-  X86_REG_DS,
-  X86_REG_ES,
-  X86_REG_FS,
-  X86_REG_GS,
+  X86_REG_ECX = 1,
+  X86_REG_EDX = 2,
+  X86_REG_EBX = 3,
+  X86_REG_ESP = 4,
+  X86_REG_EBP = 5,
+  X86_REG_ESI = 6,
+  X86_REG_EDI = 7,
+  X86_REG_EIP = 8,
+  X86_REG_EFL = 9,
+  X86_REG_CS = 10,
+  X86_REG_SS = 11,
+  X86_REG_DS = 12,
+  X86_REG_ES = 13,
+  X86_REG_FS = 14,
+  X86_REG_GS = 15,
   X86_REG_LAST,
 
   X86_REG_SP = X86_REG_ESP,
   X86_REG_PC = X86_REG_EIP,
 };
 
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
 enum X86_64Reg : uint16_t {
   X86_64_REG_RAX = 0,
-  X86_64_REG_RDX,
-  X86_64_REG_RCX,
-  X86_64_REG_RBX,
-  X86_64_REG_RSI,
-  X86_64_REG_RDI,
-  X86_64_REG_RBP,
-  X86_64_REG_RSP,
-  X86_64_REG_R8,
-  X86_64_REG_R9,
-  X86_64_REG_R10,
-  X86_64_REG_R11,
-  X86_64_REG_R12,
-  X86_64_REG_R13,
-  X86_64_REG_R14,
-  X86_64_REG_R15,
-  X86_64_REG_RIP,
+  X86_64_REG_RDX = 1,
+  X86_64_REG_RCX = 2,
+  X86_64_REG_RBX = 3,
+  X86_64_REG_RSI = 4,
+  X86_64_REG_RDI = 5,
+  X86_64_REG_RBP = 6,
+  X86_64_REG_RSP = 7,
+  X86_64_REG_R8 = 8,
+  X86_64_REG_R9 = 9,
+  X86_64_REG_R10 = 10,
+  X86_64_REG_R11 = 11,
+  X86_64_REG_R12 = 12,
+  X86_64_REG_R13 = 13,
+  X86_64_REG_R14 = 14,
+  X86_64_REG_R15 = 15,
+  X86_64_REG_RIP = 16,
   X86_64_REG_LAST,
 
   X86_64_REG_SP = X86_64_REG_RSP,
   X86_64_REG_PC = X86_64_REG_RIP,
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 051f700..96f2cb4 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -14,73 +14,108 @@
  * limitations under the License.
  */
 
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <memory>
 #include <string>
 
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Maps.h"
-#include "Memory.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
 
-Memory* MapInfo::CreateMemory(pid_t pid) {
+namespace unwindstack {
+
+Memory* MapInfo::GetFileMemory() {
+  std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
+  if (offset == 0) {
+    if (memory->Init(name, 0)) {
+      return memory.release();
+    }
+    return nullptr;
+  }
+
+  // There are two possibilities when the offset is non-zero.
+  // - There is an elf file embedded in a file.
+  // - The whole file is an elf file, and the offset needs to be saved.
+  //
+  // Map in just the part of the file for the map. If this is not
+  // a valid elf, then reinit as if the whole file is an elf file.
+  // If the offset is a valid elf, then determine the size of the map
+  // and reinit to that size. This is needed because the dynamic linker
+  // only maps in a portion of the original elf, and never the symbol
+  // file data.
+  uint64_t map_size = end - start;
+  if (!memory->Init(name, offset, map_size)) {
+    return nullptr;
+  }
+
+  bool valid;
+  uint64_t max_size;
+  Elf::GetInfo(memory.get(), &valid, &max_size);
+  if (!valid) {
+    // Init as if the whole file is an elf.
+    if (memory->Init(name, 0)) {
+      elf_offset = offset;
+      return memory.release();
+    }
+    return nullptr;
+  }
+
+  if (max_size > map_size) {
+    if (memory->Init(name, offset, max_size)) {
+      return memory.release();
+    }
+    // Try to reinit using the default map_size.
+    if (memory->Init(name, offset, map_size)) {
+      return memory.release();
+    }
+    return nullptr;
+  }
+  return memory.release();
+}
+
+Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
   if (end <= start) {
     return nullptr;
   }
 
   elf_offset = 0;
 
+  // Fail on device maps.
+  if (flags & MAPS_FLAGS_DEVICE_MAP) {
+    return nullptr;
+  }
+
   // First try and use the file associated with the info.
   if (!name.empty()) {
-    // Fail on device maps.
-    if (flags & MAPS_FLAGS_DEVICE_MAP) {
-      return nullptr;
-    }
-
-    std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
-    uint64_t map_size;
-    if (offset != 0) {
-      // Only map in a piece of the file.
-      map_size = end - start;
-    } else {
-      map_size = UINT64_MAX;
-    }
-    if (file_memory->Init(name, offset, map_size)) {
-      // It's possible that a non-zero offset might not be pointing to
-      // valid elf data. Check if this is a valid elf, and if not assume
-      // that this was meant to incorporate the entire file.
-      if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
-        // Don't bother checking the validity that will happen on the elf init.
-        if (file_memory->Init(name, 0)) {
-          elf_offset = offset;
-          return file_memory.release();
-        }
-        // Fall through if the init fails.
-      } else {
-        return file_memory.release();
-      }
+    Memory* memory = GetFileMemory();
+    if (memory != nullptr) {
+      return memory;
     }
   }
 
-  Memory* memory = nullptr;
-  if (pid == getpid()) {
-    memory = new MemoryLocal();
-  } else {
-    memory = new MemoryRemote(pid);
+  // If the map isn't readable, don't bother trying to read from process memory.
+  if (!(flags & PROT_READ)) {
+    return nullptr;
   }
-  return new MemoryRange(memory, start, end);
+  return new MemoryRange(process_memory, start, end);
 }
 
-Elf* MapInfo::GetElf(pid_t pid, bool) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
   if (elf) {
     return elf;
   }
 
-  elf = new Elf(CreateMemory(pid));
-  elf->Init();
+  elf = new Elf(CreateMemory(process_memory));
+  if (elf->Init() && init_gnu_debugdata) {
+    elf->InitGnuDebugdata();
+  }
   // If the init fails, keep the elf around as an invalid object so we
   // don't try to reinit the object.
   return elf;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index b369c43..5e1c0a2 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -25,11 +25,16 @@
 
 #include <android-base/unique_fd.h>
 
+#include <cctype>
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "Maps.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 MapInfo* Maps::Find(uint64_t pc) {
   if (maps_.empty()) {
@@ -51,61 +56,151 @@
   return nullptr;
 }
 
-bool Maps::ParseLine(const char* line, MapInfo* map_info) {
-  char permissions[5];
-  int name_pos;
-  // Linux /proc/<pid>/maps lines:
+// Assumes that line does not end in '\n'.
+static bool InternalParseLine(const char* line, MapInfo* map_info) {
+  // Do not use a sscanf implementation since it is not performant.
+
+  // Example linux /proc/<pid>/maps lines:
   // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
-  if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
-             &map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
+  char* str;
+  const char* old_str = line;
+  map_info->start = strtoul(old_str, &str, 16);
+  if (old_str == str || *str++ != '-') {
     return false;
   }
-  map_info->flags = PROT_NONE;
-  if (permissions[0] == 'r') {
-    map_info->flags |= PROT_READ;
-  }
-  if (permissions[1] == 'w') {
-    map_info->flags |= PROT_WRITE;
-  }
-  if (permissions[2] == 'x') {
-    map_info->flags |= PROT_EXEC;
+
+  old_str = str;
+  map_info->end = strtoul(old_str, &str, 16);
+  if (old_str == str || !std::isspace(*str++)) {
+    return false;
   }
 
-  map_info->name = &line[name_pos];
-  size_t length = map_info->name.length() - 1;
-  if (map_info->name[length] == '\n') {
-    map_info->name.erase(length);
+  while (std::isspace(*str)) {
+    str++;
   }
-  // Mark a device map in /dev/and not in /dev/ashmem/ specially.
-  if (!map_info->name.empty() && map_info->name.substr(0, 5) == "/dev/" &&
-      map_info->name.substr(5, 7) != "ashmem/") {
+
+  // Parse permissions data.
+  if (*str == '\0') {
+    return false;
+  }
+  map_info->flags = 0;
+  if (*str == 'r') {
+    map_info->flags |= PROT_READ;
+  } else if (*str != '-') {
+    return false;
+  }
+  str++;
+  if (*str == 'w') {
+    map_info->flags |= PROT_WRITE;
+  } else if (*str != '-') {
+    return false;
+  }
+  str++;
+  if (*str == 'x') {
+    map_info->flags |= PROT_EXEC;
+  } else if (*str != '-') {
+    return false;
+  }
+  str++;
+  if (*str != 'p' && *str != 's') {
+    return false;
+  }
+  str++;
+
+  if (!std::isspace(*str++)) {
+    return false;
+  }
+
+  old_str = str;
+  map_info->offset = strtoul(old_str, &str, 16);
+  if (old_str == str || !std::isspace(*str)) {
+    return false;
+  }
+
+  // Ignore the 00:00 values.
+  old_str = str;
+  (void)strtoul(old_str, &str, 16);
+  if (old_str == str || *str++ != ':') {
+    return false;
+  }
+  if (std::isspace(*str)) {
+    return false;
+  }
+
+  // Skip the inode.
+  old_str = str;
+  (void)strtoul(str, &str, 16);
+  if (old_str == str || !std::isspace(*str++)) {
+    return false;
+  }
+
+  // Skip decimal digit.
+  old_str = str;
+  (void)strtoul(old_str, &str, 10);
+  if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
+    return false;
+  }
+
+  while (std::isspace(*str)) {
+    str++;
+  }
+  if (*str == '\0') {
+    map_info->name = str;
+    return true;
+  }
+
+  // Save the name data.
+  map_info->name = str;
+
+  // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
+  if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
     map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
   }
-
   return true;
 }
 
 bool Maps::Parse() {
-  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
-  if (!fp) {
+  int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
     return false;
   }
 
-  bool valid = true;
-  char* line = nullptr;
-  size_t line_len;
-  while (getline(&line, &line_len, fp.get()) > 0) {
-    MapInfo map_info;
-    if (!ParseLine(line, &map_info)) {
-      valid = false;
+  bool return_value = true;
+  char buffer[2048];
+  size_t leftover = 0;
+  while (true) {
+    ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
+    if (bytes == -1) {
+      return_value = false;
       break;
     }
+    if (bytes == 0) {
+      break;
+    }
+    bytes += leftover;
+    char* line = buffer;
+    while (bytes > 0) {
+      char* newline = static_cast<char*>(memchr(line, '\n', bytes));
+      if (newline == nullptr) {
+        memmove(buffer, line, bytes);
+        break;
+      }
+      *newline = '\0';
 
-    maps_.push_back(map_info);
+      MapInfo map_info;
+      if (!InternalParseLine(line, &map_info)) {
+        return_value = false;
+        break;
+      }
+      maps_.push_back(map_info);
+
+      bytes -= newline - line + 1;
+      line = newline + 1;
+    }
+    leftover = bytes;
   }
-  free(line);
-
-  return valid;
+  close(fd);
+  return return_value;
 }
 
 Maps::~Maps() {
@@ -123,12 +218,12 @@
     if (end_of_line == nullptr) {
       line = start_of_line;
     } else {
-      end_of_line++;
       line = std::string(start_of_line, end_of_line - start_of_line);
+      end_of_line++;
     }
 
     MapInfo map_info;
-    if (!ParseLine(line.c_str(), &map_info)) {
+    if (!InternalParseLine(line.c_str(), &map_info)) {
       return false;
     }
     maps_.push_back(map_info);
@@ -194,3 +289,5 @@
   }
   return true;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 9e46509..32753df 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -28,7 +28,11 @@
 
 #include <android-base/unique_fd.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+
+namespace unwindstack {
 
 bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
   string->clear();
@@ -48,6 +52,13 @@
   return false;
 }
 
+std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
+  if (pid == getpid()) {
+    return std::shared_ptr<Memory>(new MemoryLocal());
+  }
+  return std::shared_ptr<Memory>(new MemoryRemote(pid));
+}
+
 bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
   uint64_t last_read_byte;
   if (__builtin_add_overflow(size, addr, &last_read_byte)) {
@@ -245,6 +256,11 @@
   return true;
 }
 
+MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end)
+    : memory_(memory), begin_(begin), length_(end - begin) {
+  CHECK(end > begin);
+}
+
 bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
   uint64_t max_read;
   if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
@@ -253,3 +269,5 @@
   // The check above guarantees that addr + begin_ will not overflow.
   return memory_->Read(addr + begin_, dst, size);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index e7d10b2..69e6512 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <elf.h>
 #include <stdint.h>
 #include <sys/ptrace.h>
@@ -22,46 +21,25 @@
 
 #include <vector>
 
-#include "Elf.h"
-#include "ElfInterface.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "Check.h"
 #include "Machine.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include "Ucontext.h"
 #include "User.h"
 
-template <typename AddressType>
-uint64_t RegsImpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
-  uint64_t load_bias = 0;
-  if (elf->valid()) {
-    load_bias = elf->interface()->load_bias();
-  }
-
-  return pc_ - map_info->start + load_bias + map_info->elf_offset;
-}
-
-template <typename AddressType>
-bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
-  switch (return_loc_.type) {
-  case LOCATION_REGISTER:
-    assert(return_loc_.value < total_regs_);
-    *value = regs_[return_loc_.value];
-    return true;
-  case LOCATION_SP_OFFSET:
-    AddressType return_value;
-    if (!memory->Read(sp_ + return_loc_.value, &return_value, sizeof(return_value))) {
-      return false;
-    }
-    *value = return_value;
-    return true;
-  case LOCATION_UNKNOWN:
-  default:
-    return false;
-  }
-}
+namespace unwindstack {
 
 RegsArm::RegsArm()
     : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
 
+uint32_t RegsArm::MachineType() {
+  return EM_ARM;
+}
+
 uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
     return rel_pc;
@@ -88,9 +66,27 @@
   return rel_pc - 4;
 }
 
+void RegsArm::SetFromRaw() {
+  set_pc(regs_[ARM_REG_PC]);
+  set_sp(regs_[ARM_REG_SP]);
+}
+
+bool RegsArm::SetPcFromReturnAddress(Memory*) {
+  if (pc() == regs_[ARM_REG_LR]) {
+    return false;
+  }
+
+  set_pc(regs_[ARM_REG_LR]);
+  return true;
+}
+
 RegsArm64::RegsArm64()
     : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
 
+uint32_t RegsArm64::MachineType() {
+  return EM_AARCH64;
+}
+
 uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
     return rel_pc;
@@ -102,9 +98,27 @@
   return rel_pc - 4;
 }
 
+void RegsArm64::SetFromRaw() {
+  set_pc(regs_[ARM64_REG_PC]);
+  set_sp(regs_[ARM64_REG_SP]);
+}
+
+bool RegsArm64::SetPcFromReturnAddress(Memory*) {
+  if (pc() == regs_[ARM64_REG_LR]) {
+    return false;
+  }
+
+  set_pc(regs_[ARM64_REG_LR]);
+  return true;
+}
+
 RegsX86::RegsX86()
     : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
 
+uint32_t RegsX86::MachineType() {
+  return EM_386;
+}
+
 uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
     return rel_pc;
@@ -116,9 +130,29 @@
   return rel_pc - 1;
 }
 
+void RegsX86::SetFromRaw() {
+  set_pc(regs_[X86_REG_PC]);
+  set_sp(regs_[X86_REG_SP]);
+}
+
+bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
+  // Attempt to get the return address from the top of the stack.
+  uint32_t new_pc;
+  if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+    return false;
+  }
+
+  set_pc(new_pc);
+  return true;
+}
+
 RegsX86_64::RegsX86_64()
     : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
 
+uint32_t RegsX86_64::MachineType() {
+  return EM_X86_64;
+}
+
 uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
   if (!elf->valid()) {
     return rel_pc;
@@ -131,15 +165,28 @@
   return rel_pc - 1;
 }
 
+void RegsX86_64::SetFromRaw() {
+  set_pc(regs_[X86_64_REG_PC]);
+  set_sp(regs_[X86_64_REG_SP]);
+}
+
+bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
+  // Attempt to get the return address from the top of the stack.
+  uint64_t new_pc;
+  if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+    return false;
+  }
+
+  set_pc(new_pc);
+  return true;
+}
+
 static Regs* ReadArm(void* remote_data) {
   arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
 
   RegsArm* regs = new RegsArm();
   memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-
-  regs->set_pc(user->regs[ARM_REG_PC]);
-  regs->set_sp(user->regs[ARM_REG_SP]);
-
+  regs->SetFromRaw();
   return regs;
 }
 
@@ -148,9 +195,10 @@
 
   RegsArm64* regs = new RegsArm64();
   memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
-  regs->set_pc(user->pc);
-  regs->set_sp(user->sp);
-
+  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+  reg_data[ARM64_REG_PC] = user->pc;
+  reg_data[ARM64_REG_SP] = user->sp;
+  regs->SetFromRaw();
   return regs;
 }
 
@@ -168,9 +216,7 @@
   (*regs)[X86_REG_ESP] = user->esp;
   (*regs)[X86_REG_EIP] = user->eip;
 
-  regs->set_pc(user->eip);
-  regs->set_sp(user->esp);
-
+  regs->SetFromRaw();
   return regs;
 }
 
@@ -196,15 +242,13 @@
   (*regs)[X86_64_REG_RSP] = user->rsp;
   (*regs)[X86_64_REG_RIP] = user->rip;
 
-  regs->set_pc(user->rip);
-  regs->set_sp(user->rsp);
-
+  regs->SetFromRaw();
   return regs;
 }
 
 // This function assumes that reg_data is already aligned to a 64 bit value.
 // If not this could crash with an unaligned access.
-Regs* Regs::RemoteGet(pid_t pid, uint32_t* machine_type) {
+Regs* Regs::RemoteGet(pid_t pid) {
   // Make the buffer large enough to contain the largest registers type.
   std::vector<uint64_t> buffer(MAX_USER_REGS_SIZE / sizeof(uint64_t));
   struct iovec io;
@@ -217,17 +261,312 @@
 
   switch (io.iov_len) {
   case sizeof(x86_user_regs):
-    *machine_type = EM_386;
     return ReadX86(buffer.data());
   case sizeof(x86_64_user_regs):
-    *machine_type = EM_X86_64;
     return ReadX86_64(buffer.data());
   case sizeof(arm_user_regs):
-    *machine_type = EM_ARM;
     return ReadArm(buffer.data());
   case sizeof(arm64_user_regs):
-    *machine_type = EM_AARCH64;
     return ReadArm64(buffer.data());
   }
   return nullptr;
 }
+
+static Regs* CreateFromArmUcontext(void* ucontext) {
+  arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+  RegsArm* regs = new RegsArm();
+  memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+  regs->SetFromRaw();
+  return regs;
+}
+
+static Regs* CreateFromArm64Ucontext(void* ucontext) {
+  arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+  RegsArm64* regs = new RegsArm64();
+  memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+  regs->SetFromRaw();
+  return regs;
+}
+
+void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
+  // Put the registers in the expected order.
+  regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
+  regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
+  regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
+  regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
+  regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
+  regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
+  regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
+  regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
+  regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
+  SetFromRaw();
+}
+
+static Regs* CreateFromX86Ucontext(void* ucontext) {
+  x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+  RegsX86* regs = new RegsX86();
+  regs->SetFromUcontext(x86_ucontext);
+  return regs;
+}
+
+void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
+  // R8-R15
+  memcpy(&regs_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+  // Rest of the registers.
+  regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
+  regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
+  regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
+  regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
+  regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
+  regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
+  regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
+  regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
+  regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
+
+  SetFromRaw();
+}
+
+static Regs* CreateFromX86_64Ucontext(void* ucontext) {
+  x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+  RegsX86_64* regs = new RegsX86_64();
+  regs->SetFromUcontext(x86_64_ucontext);
+  return regs;
+}
+
+Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) {
+  switch (machine_type) {
+    case EM_386:
+      return CreateFromX86Ucontext(ucontext);
+    case EM_X86_64:
+      return CreateFromX86_64Ucontext(ucontext);
+    case EM_ARM:
+      return CreateFromArmUcontext(ucontext);
+    case EM_AARCH64:
+      return CreateFromArm64Ucontext(ucontext);
+  }
+  return nullptr;
+}
+
+uint32_t Regs::CurrentMachineType() {
+#if defined(__arm__)
+  return EM_ARM;
+#elif defined(__aarch64__)
+  return EM_AARCH64;
+#elif defined(__i386__)
+  return EM_386;
+#elif defined(__x86_64__)
+  return EM_X86_64;
+#else
+  abort();
+#endif
+}
+
+Regs* Regs::CreateFromLocal() {
+  Regs* regs;
+#if defined(__arm__)
+  regs = new RegsArm();
+#elif defined(__aarch64__)
+  regs = new RegsArm64();
+#elif defined(__i386__)
+  regs = new RegsX86();
+#elif defined(__x86_64__)
+  regs = new RegsX86_64();
+#else
+  abort();
+#endif
+  return regs;
+}
+
+bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint32_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;
+  }
+
+  uint64_t offset = 0;
+  if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+    // non-RT sigreturn call.
+    // __restore:
+    //
+    // Form 1 (arm):
+    // 0x77 0x70              mov r7, #0x77
+    // 0xa0 0xe3              svc 0x00000000
+    //
+    // Form 2 (arm):
+    // 0x77 0x00 0x90 0xef    svc 0x00900077
+    //
+    // Form 3 (thumb):
+    // 0x77 0x27              movs r7, #77
+    // 0x00 0xdf              svc 0
+    if (!process_memory->Read(sp(), &data, sizeof(data))) {
+      return false;
+    }
+    if (data == 0x5ac3c35a) {
+      // SP + uc_mcontext offset + r0 offset.
+      offset = sp() + 0x14 + 0xc;
+    } else {
+      // SP + r0 offset
+      offset = sp() + 0xc;
+    }
+  } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+    // RT sigreturn call.
+    // __restore_rt:
+    //
+    // Form 1 (arm):
+    // 0xad 0x70      mov r7, #0xad
+    // 0xa0 0xe3      svc 0x00000000
+    //
+    // Form 2 (arm):
+    // 0xad 0x00 0x90 0xef    svc 0x009000ad
+    //
+    // Form 3 (thumb):
+    // 0xad 0x27              movs r7, #ad
+    // 0x00 0xdf              svc 0
+    if (!process_memory->Read(sp(), &data, sizeof(data))) {
+      return false;
+    }
+    if (data == sp() + 8) {
+      // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+      offset = sp() + 8 + 0x80 + 0x14 + 0xc;
+    } else {
+      // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+      offset = sp() + 0x80 + 0x14 + 0xc;
+    }
+  }
+  if (offset == 0) {
+    return false;
+  }
+
+  if (!process_memory->Read(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
+    return false;
+  }
+  SetFromRaw();
+  return true;
+}
+
+bool RegsArm64::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.
+  // __kernel_rt_sigreturn:
+  // 0xd2801168     mov x8, #0x8b
+  // 0xd4000001     svc #0x0
+  if (data != 0xd4000001d2801168ULL) {
+    return false;
+  }
+
+  // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
+  if (!process_memory->Read(sp() + 0x80 + 0xb0 + 0x08, regs_.data(),
+                            sizeof(uint64_t) * ARM64_REG_LAST)) {
+    return false;
+  }
+
+  SetFromRaw();
+  return true;
+}
+
+bool RegsX86::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;
+  }
+
+  if (data == 0x80cd00000077b858ULL) {
+    // Without SA_SIGINFO set, the return sequence is:
+    //
+    //   __restore:
+    //   0x58                            pop %eax
+    //   0xb8 0x77 0x00 0x00 0x00        movl 0x77,%eax
+    //   0xcd 0x80                       int 0x80
+    //
+    // SP points at arguments:
+    //   int signum
+    //   struct sigcontext (same format as mcontext)
+    struct x86_mcontext_t context;
+    if (!process_memory->Read(sp() + 4, &context, sizeof(context))) {
+      return false;
+    }
+    regs_[X86_REG_EBP] = context.ebp;
+    regs_[X86_REG_ESP] = context.esp;
+    regs_[X86_REG_EBX] = context.ebx;
+    regs_[X86_REG_EDX] = context.edx;
+    regs_[X86_REG_ECX] = context.ecx;
+    regs_[X86_REG_EAX] = context.eax;
+    regs_[X86_REG_EIP] = context.eip;
+    SetFromRaw();
+    return true;
+  } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
+    // With SA_SIGINFO set, the return sequence is:
+    //
+    //   __restore_rt:
+    //   0xb8 0xad 0x00 0x00 0x00        movl 0xad,%eax
+    //   0xcd 0x80                       int 0x80
+    //
+    // SP points at arguments:
+    //   int signum
+    //   siginfo*
+    //   ucontext*
+
+    // Get the location of the sigcontext data.
+    uint32_t ptr;
+    if (!process_memory->Read(sp() + 8, &ptr, sizeof(ptr))) {
+      return false;
+    }
+    // Only read the portion of the data structure we care about.
+    x86_ucontext_t x86_ucontext;
+    if (!process_memory->Read(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
+      return false;
+    }
+    SetFromUcontext(&x86_ucontext);
+    return true;
+  }
+  return false;
+}
+
+bool RegsX86_64::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)) || data != 0x0f0000000fc0c748) {
+    return false;
+  }
+
+  uint16_t data2;
+  if (!elf_memory->Read(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+    return false;
+  }
+
+  // __restore_rt:
+  // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00   mov $0xf,%rax
+  // 0x0f 0x05                            syscall
+  // 0x0f                                 nopl 0x0($rax)
+
+  // Read the mcontext data from the stack.
+  // sp points to the ucontext data structure, read only the mcontext part.
+  x86_64_ucontext_t x86_64_ucontext;
+  if (!process_memory->Read(sp() + 0x28, &x86_64_ucontext.uc_mcontext, sizeof(x86_64_mcontext_t))) {
+    return false;
+  }
+  SetFromUcontext(&x86_64_ucontext);
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 86c1233..42d816a 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <elf.h>
 #include <stdint.h>
 
 #include <string>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
 #include "Symbols.h"
 
+namespace unwindstack {
+
 Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
                  uint64_t str_size)
     : cur_offset_(offset),
@@ -58,7 +61,7 @@
   if (symbols_.size() != 0) {
     const Info* info = GetInfoFromCache(addr);
     if (info) {
-      assert(addr >= info->start_offset && addr <= info->end_offset);
+      CHECK(addr >= info->start_offset && addr <= info->end_offset);
       *func_offset = addr - info->start_offset;
       return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
     }
@@ -108,3 +111,5 @@
 // Instantiate all of the needed template functions.
 template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
 template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 3c0d033..689144b 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -22,6 +22,8 @@
 #include <string>
 #include <vector>
 
+namespace unwindstack {
+
 // Forward declaration.
 class Memory;
 
@@ -61,4 +63,6 @@
   std::vector<Info> symbols_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h
new file mode 100644
index 0000000..22f6a89
--- /dev/null
+++ b/libunwindstack/Ucontext.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 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_H
+#define _LIBUNWINDSTACK_UCONTEXT_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+//-------------------------------------------------------------------
+// ARM ucontext structures
+//-------------------------------------------------------------------
+struct arm_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct arm_mcontext_t {
+  uint32_t trap_no;             // unsigned long
+  uint32_t error_code;          // unsigned long
+  uint32_t oldmask;             // unsigned long
+  uint32_t regs[ARM_REG_LAST];  // unsigned long
+  uint32_t cpsr;                // unsigned long
+  uint32_t fault_address;       // unsigned long
+};
+
+struct arm_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  arm_stack_t uc_stack;
+  arm_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// ARM64 ucontext structures
+//-------------------------------------------------------------------
+struct arm64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint64_t ss_size;  // size_t
+};
+
+struct arm64_sigset_t {
+  uint64_t sig;  // unsigned long
+};
+
+struct arm64_mcontext_t {
+  uint64_t fault_address;         // __u64
+  uint64_t regs[ARM64_REG_LAST];  // __u64
+  uint64_t pstate;                // __u64
+  // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  arm64_stack_t uc_stack;
+  arm64_sigset_t uc_sigmask;
+  // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+  char __padding[128 - sizeof(arm64_sigset_t)];
+  // The full structure requires 16 byte alignment, but our partial structure
+  // doesn't, so force the alignment.
+  arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86 ucontext structures
+//-------------------------------------------------------------------
+struct x86_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct x86_mcontext_t {
+  uint32_t gs;
+  uint32_t fs;
+  uint32_t es;
+  uint32_t ds;
+  uint32_t edi;
+  uint32_t esi;
+  uint32_t ebp;
+  uint32_t esp;
+  uint32_t ebx;
+  uint32_t edx;
+  uint32_t ecx;
+  uint32_t eax;
+  uint32_t trapno;
+  uint32_t err;
+  uint32_t eip;
+  uint32_t cs;
+  uint32_t efl;
+  uint32_t uesp;
+  uint32_t ss;
+  // Only care about the registers, skip everything else.
+};
+
+struct x86_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  x86_stack_t uc_stack;
+  x86_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86_64 ucontext structures
+//-------------------------------------------------------------------
+struct x86_64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint64_t ss_size;  // size_t
+};
+
+struct x86_64_mcontext_t {
+  uint64_t r8;
+  uint64_t r9;
+  uint64_t r10;
+  uint64_t r11;
+  uint64_t r12;
+  uint64_t r13;
+  uint64_t r14;
+  uint64_t r15;
+  uint64_t rdi;
+  uint64_t rsi;
+  uint64_t rbp;
+  uint64_t rbx;
+  uint64_t rdx;
+  uint64_t rax;
+  uint64_t rcx;
+  uint64_t rsp;
+  uint64_t rip;
+  uint64_t efl;
+  uint64_t csgsfs;
+  uint64_t err;
+  uint64_t trapno;
+  uint64_t oldmask;
+  uint64_t cr2;
+  // Only care about the registers, skip everything else.
+};
+
+struct x86_64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  x86_64_stack_t uc_stack;
+  x86_64_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_H
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
new file mode 100644
index 0000000..15a8893
--- /dev/null
+++ b/libunwindstack/Unwinder.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 <elf.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Unwinder.h>
+
+namespace unwindstack {
+
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
+  size_t frame_num = frames_.size();
+  frames_.resize(frame_num + 1);
+  FrameData* frame = &frames_.at(frame_num);
+  frame->num = frame_num;
+  frame->pc = regs_->pc();
+  frame->sp = regs_->sp();
+  frame->rel_pc = rel_pc;
+
+  if (map_info == nullptr) {
+    return;
+  }
+
+  if (adjust_pc) {
+    // Don't adjust the first frame pc.
+    frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
+
+    // Adjust the original pc.
+    frame->pc -= rel_pc - frame->rel_pc;
+  }
+
+  frame->map_name = map_info->name;
+  frame->map_offset = map_info->offset;
+  frame->map_start = map_info->start;
+  frame->map_end = map_info->end;
+  frame->map_flags = map_info->flags;
+  frame->map_load_bias = elf->GetLoadBias();
+
+  if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
+    frame->function_name = "";
+    frame->function_offset = 0;
+  }
+}
+
+void Unwinder::Unwind(std::set<std::string>* initial_map_names_to_skip) {
+  frames_.clear();
+
+  bool return_address_attempt = false;
+  bool adjust_pc = false;
+  for (; frames_.size() < max_frames_;) {
+    MapInfo* map_info = maps_->Find(regs_->pc());
+
+    uint64_t rel_pc;
+    Elf* elf;
+    if (map_info == nullptr) {
+      rel_pc = regs_->pc();
+    } else {
+      elf = map_info->GetElf(process_memory_, true);
+      rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+    }
+
+    if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
+        initial_map_names_to_skip->find(basename(map_info->name.c_str())) ==
+            initial_map_names_to_skip->end()) {
+      FillInFrame(map_info, elf, rel_pc, adjust_pc);
+      // Once a frame is added, stop skipping frames.
+      initial_map_names_to_skip = nullptr;
+    }
+    adjust_pc = true;
+
+    bool stepped;
+    bool in_device_map = false;
+    if (map_info == nullptr) {
+      stepped = false;
+    } else {
+      if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+        // Do not stop here, fall through in case we are
+        // in the speculative unwind path and need to remove
+        // some of the speculative frames.
+        stepped = false;
+        in_device_map = true;
+      } else {
+        MapInfo* sp_info = maps_->Find(regs_->sp());
+        if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+          // Do not stop here, fall through in case we are
+          // in the speculative unwind path and need to remove
+          // some of the speculative frames.
+          stepped = false;
+          in_device_map = true;
+        } else {
+          bool finished;
+          stepped =
+              elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(), &finished);
+          if (stepped && finished) {
+            break;
+          }
+        }
+      }
+    }
+    if (!stepped) {
+      if (return_address_attempt) {
+        // Remove the speculative frame.
+        frames_.pop_back();
+        break;
+      } else if (in_device_map) {
+        // Do not attempt any other unwinding, pc or sp is in a device
+        // map.
+        break;
+      } else {
+        // Steping didn't work, try this secondary method.
+        if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
+          break;
+        }
+        return_address_attempt = true;
+      }
+    } else {
+      return_address_attempt = false;
+    }
+  }
+}
+
+std::string Unwinder::FormatFrame(size_t frame_num) {
+  if (frame_num >= frames_.size()) {
+    return "";
+  }
+  return FormatFrame(frames_[frame_num],
+                     regs_->MachineType() == EM_ARM || regs_->MachineType() == EM_386);
+}
+
+std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
+  std::string data;
+
+  if (bits32) {
+    data += android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
+  } else {
+    data += android::base::StringPrintf("  #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
+  }
+
+  if (frame.map_offset != 0) {
+    data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
+  }
+
+  if (frame.map_start == frame.map_end) {
+    // No valid map associated with this frame.
+    data += "  <unknown>";
+  } else if (!frame.map_name.empty()) {
+    data += "  " + frame.map_name;
+  } else {
+    data += android::base::StringPrintf("  <anonymous:%" PRIx64 ">", frame.map_start);
+  }
+  if (!frame.function_name.empty()) {
+    data += " (" + frame.function_name;
+    if (frame.function_offset != 0) {
+      data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+    }
+    data += ')';
+  }
+  return data;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/User.h b/libunwindstack/User.h
index a695467..53f7e50 100644
--- a/libunwindstack/User.h
+++ b/libunwindstack/User.h
@@ -29,6 +29,8 @@
 #ifndef _LIBUNWINDSTACK_USER_H
 #define _LIBUNWINDSTACK_USER_H
 
+namespace unwindstack {
+
 struct x86_user_regs {
   uint32_t ebx;
   uint32_t ecx;
@@ -93,4 +95,6 @@
 // The largest user structure.
 constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_USER_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
similarity index 95%
rename from libunwindstack/DwarfLocation.h
rename to libunwindstack/include/unwindstack/DwarfLocation.h
index 062d125..3467e6a 100644
--- a/libunwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -21,6 +21,8 @@
 
 #include <unordered_map>
 
+namespace unwindstack {
+
 enum DwarfLocationEnum : uint8_t {
   DWARF_LOCATION_INVALID = 0,
   DWARF_LOCATION_UNDEFINED,
@@ -38,4 +40,6 @@
 
 typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
similarity index 97%
rename from libunwindstack/DwarfMemory.h
rename to libunwindstack/include/unwindstack/DwarfMemory.h
index a304dd9..8dd8d2b 100644
--- a/libunwindstack/DwarfMemory.h
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Memory;
 
@@ -69,4 +71,6 @@
   uint64_t text_offset_ = static_cast<uint64_t>(-1);
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
similarity index 89%
rename from libunwindstack/DwarfSection.h
rename to libunwindstack/include/unwindstack/DwarfSection.h
index e617601..1e843c3 100644
--- a/libunwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -22,18 +22,20 @@
 #include <iterator>
 #include <unordered_map>
 
-#include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
 
 // Forward declarations.
+enum DwarfError : uint8_t;
 class Memory;
 class Regs;
 
 class DwarfSection {
  public:
-  DwarfSection(Memory* memory) : memory_(memory) {}
+  DwarfSection(Memory* memory);
   virtual ~DwarfSection() = default;
 
   class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
@@ -74,7 +76,7 @@
 
   virtual bool Init(uint64_t offset, uint64_t size) = 0;
 
-  virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
+  virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
 
   virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
 
@@ -98,13 +100,13 @@
 
   virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
 
-  bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
 
  protected:
   DwarfMemory memory_;
-  DwarfError last_error_ = DWARF_ERROR_NONE;
+  DwarfError last_error_;
 
-  uint64_t fde_count_;
+  uint64_t fde_count_ = 0;
   std::unordered_map<uint64_t, DwarfFde> fde_entries_;
   std::unordered_map<uint64_t, DwarfCie> cie_entries_;
   std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
@@ -117,7 +119,7 @@
   virtual ~DwarfSectionImpl() = default;
 
   bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
-            Regs* regs) override;
+            Regs* regs, bool* finished) override;
 
   const DwarfCie* GetCie(uint64_t offset);
   bool FillInCie(DwarfCie* cie);
@@ -134,4 +136,6 @@
                       AddressType* value);
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
similarity index 92%
rename from libunwindstack/DwarfStructs.h
rename to libunwindstack/include/unwindstack/DwarfStructs.h
index 87182c9..4b481f0 100644
--- a/libunwindstack/DwarfStructs.h
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -21,12 +21,12 @@
 
 #include <vector>
 
-#include "DwarfEncoding.h"
+namespace unwindstack {
 
 struct DwarfCie {
   uint8_t version = 0;
-  uint8_t fde_address_encoding = DW_EH_PE_absptr;
-  uint8_t lsda_encoding = DW_EH_PE_omit;
+  uint8_t fde_address_encoding = 0;
+  uint8_t lsda_encoding = 0;
   uint8_t segment_size = 0;
   std::vector<char> augmentation_string;
   uint64_t personality_handler = 0;
@@ -49,4 +49,6 @@
 
 constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
similarity index 68%
rename from libunwindstack/Elf.h
rename to libunwindstack/include/unwindstack/Elf.h
index 7bf45b8..f246beb 100644
--- a/libunwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -22,14 +22,17 @@
 #include <memory>
 #include <string>
 
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
 
 #if !defined(EM_AARCH64)
 #define EM_AARCH64 183
 #endif
 
+namespace unwindstack {
+
 // Forward declaration.
+struct MapInfo;
 class Regs;
 
 class Elf {
@@ -41,20 +44,18 @@
 
   void InitGnuDebugdata();
 
-  bool GetSoname(std::string* name) {
-    return valid_ && interface_->GetSoname(name);
-  }
+  bool GetSoname(std::string* name);
 
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) {
-    return false;
-  }
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
 
-  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
-    return valid_ && interface_->Step(rel_pc, regs, process_memory);
-  }
+  uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
+
+  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
+  uint64_t GetLoadBias();
+
   bool valid() { return valid_; }
 
   uint32_t machine_type() { return machine_type_; }
@@ -65,14 +66,23 @@
 
   ElfInterface* interface() { return interface_.get(); }
 
+  ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
+
   static bool IsValidElf(Memory* memory);
 
+  static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+
  protected:
   bool valid_ = false;
   std::unique_ptr<ElfInterface> interface_;
   std::unique_ptr<Memory> memory_;
   uint32_t machine_type_;
   uint8_t class_type_;
+
+  std::unique_ptr<Memory> gnu_debugdata_memory_;
+  std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
similarity index 70%
rename from libunwindstack/ElfInterface.h
rename to libunwindstack/include/unwindstack/ElfInterface.h
index 944146c..4fe966f 100644
--- a/libunwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -25,9 +25,14 @@
 #include <unordered_map>
 #include <vector>
 
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
 // Forward declarations.
 class Memory;
 class Regs;
+class Symbols;
 
 struct LoadInfo {
   uint64_t offset;
@@ -44,7 +49,7 @@
 class ElfInterface {
  public:
   ElfInterface(Memory* memory) : memory_(memory) {}
-  virtual ~ElfInterface() = default;
+  virtual ~ElfInterface();
 
   virtual bool Init() = 0;
 
@@ -54,7 +59,7 @@
 
   virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
 
-  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
+  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
   Memory* CreateGnuDebugdataMemory();
 
@@ -68,10 +73,18 @@
   uint64_t dynamic_size() { return dynamic_size_; }
   uint64_t eh_frame_offset() { return eh_frame_offset_; }
   uint64_t eh_frame_size() { return eh_frame_size_; }
+  uint64_t debug_frame_offset() { return debug_frame_offset_; }
+  uint64_t debug_frame_size() { return debug_frame_size_; }
   uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
   uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
 
+  DwarfSection* eh_frame() { return eh_frame_.get(); }
+  DwarfSection* debug_frame() { return debug_frame_.get(); }
+
  protected:
+  template <typename AddressType>
+  void InitHeadersWithTemplate();
+
   template <typename EhdrType, typename PhdrType, typename ShdrType>
   bool ReadAllHeaders();
 
@@ -84,8 +97,14 @@
   template <typename DynType>
   bool GetSonameWithTemplate(std::string* soname);
 
+  template <typename SymType>
+  bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+
   virtual bool HandleType(uint64_t, uint32_t) { return false; }
 
+  template <typename EhdrType>
+  static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
+
   Memory* memory_;
   std::unordered_map<uint64_t, LoadInfo> pt_loads_;
   uint64_t load_bias_ = 0;
@@ -105,6 +124,11 @@
 
   uint8_t soname_type_ = SONAME_UNKNOWN;
   std::string soname_;
+
+  std::unique_ptr<DwarfSection> eh_frame_;
+  std::unique_ptr<DwarfSection> debug_frame_;
+
+  std::vector<Symbols*> symbols_;
 };
 
 class ElfInterface32 : public ElfInterface {
@@ -116,15 +140,18 @@
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
   }
 
-  void InitHeaders() override {
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
-    return false;
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
+  }
+
+  static void GetMaxSize(Memory* memory, uint64_t* size) {
+    GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
   }
 };
 
@@ -137,16 +164,21 @@
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
   }
 
-  void InitHeaders() override {
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
 
   bool GetSoname(std::string* soname) override {
     return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
   }
 
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override {
-    return false;
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
+  }
+
+  static void GetMaxSize(Memory* memory, uint64_t* size) {
+    GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
   }
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
similarity index 93%
rename from libunwindstack/Log.h
rename to libunwindstack/include/unwindstack/Log.h
index 2d01aa8..aa1219c 100644
--- a/libunwindstack/Log.h
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -19,7 +19,11 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 void log_to_stdout(bool enable);
 void log(uint8_t indent, const char* format, ...);
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
similarity index 79%
rename from libunwindstack/MapInfo.h
rename to libunwindstack/include/unwindstack/MapInfo.h
index 79a2ada..f108766 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -21,6 +21,8 @@
 
 #include <string>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Elf;
 class Memory;
@@ -38,8 +40,15 @@
   // instead of a portion of the file.
   uint64_t elf_offset;
 
-  Memory* CreateMemory(pid_t pid);
-  Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
+  // This function guarantees it will never return nullptr.
+  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+
+ private:
+  Memory* GetFileMemory();
+
+  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
similarity index 92%
rename from libunwindstack/Maps.h
rename to libunwindstack/include/unwindstack/Maps.h
index 239b64a..22122a9 100644
--- a/libunwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -23,8 +23,9 @@
 #include <string>
 #include <vector>
 
-#include "Elf.h"
-#include "MapInfo.h"
+#include <unwindstack/MapInfo.h>
+
+namespace unwindstack {
 
 // Special flag to indicate a map is in /dev/. However, a map in
 // /dev/ashmem/... does not set this flag.
@@ -37,8 +38,6 @@
 
   MapInfo* Find(uint64_t pc);
 
-  bool ParseLine(const char* line, MapInfo* map_info);
-
   virtual bool Parse();
 
   virtual const std::string GetMapsFile() const { return ""; }
@@ -53,6 +52,11 @@
 
   size_t Total() { return maps_.size(); }
 
+  MapInfo* Get(size_t index) {
+    if (index >= maps_.size()) return nullptr;
+    return &maps_[index];
+  }
+
  protected:
   std::vector<MapInfo> maps_;
 };
@@ -104,4 +108,6 @@
   bool Parse() override;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
similarity index 86%
rename from libunwindstack/Memory.h
rename to libunwindstack/include/unwindstack/Memory.h
index f9f6d56..183b899 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -17,19 +17,23 @@
 #ifndef _LIBUNWINDSTACK_MEMORY_H
 #define _LIBUNWINDSTACK_MEMORY_H
 
-#include <assert.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
+namespace unwindstack {
+
 class Memory {
  public:
   Memory() = default;
   virtual ~Memory() = default;
 
+  static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+
   virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
 
   virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
@@ -46,13 +50,9 @@
     return Read(offset, field, size);
   }
 
-  inline bool Read32(uint64_t addr, uint32_t* dst) {
-    return Read(addr, dst, sizeof(uint32_t));
-  }
+  inline bool Read32(uint64_t addr, uint32_t* dst) { return Read(addr, dst, sizeof(uint32_t)); }
 
-  inline bool Read64(uint64_t addr, uint64_t* dst) {
-    return Read(addr, dst, sizeof(uint64_t));
-  }
+  inline bool Read64(uint64_t addr, uint64_t* dst) { return Read(addr, dst, sizeof(uint64_t)); }
 };
 
 class MemoryBuffer : public Memory {
@@ -128,18 +128,17 @@
 
 class MemoryRange : public Memory {
  public:
-  MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
-      : memory_(memory), begin_(begin), length_(end - begin) {
-    assert(end > begin);
-  }
-  virtual ~MemoryRange() { delete memory_; }
+  MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end);
+  virtual ~MemoryRange() = default;
 
   bool Read(uint64_t addr, void* dst, size_t size) override;
 
  private:
-  Memory* memory_;
+  std::shared_ptr<Memory> memory_;
   uint64_t begin_;
   uint64_t length_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
similarity index 64%
rename from libunwindstack/Regs.h
rename to libunwindstack/include/unwindstack/Regs.h
index 8f5a721..9d3150b 100644
--- a/libunwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -21,9 +21,14 @@
 
 #include <vector>
 
+namespace unwindstack {
+
 // Forward declarations.
 class Elf;
 struct MapInfo;
+class Memory;
+struct x86_ucontext_t;
+struct x86_64_ucontext_t;
 
 class Regs {
  public:
@@ -44,20 +49,27 @@
       : total_regs_(total_regs), sp_reg_(sp_reg), return_loc_(return_loc) {}
   virtual ~Regs() = default;
 
+  virtual uint32_t MachineType() = 0;
+
   virtual void* RawData() = 0;
   virtual uint64_t pc() = 0;
   virtual uint64_t sp() = 0;
 
-  virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;
-
-  virtual uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) = 0;
-
   virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
 
+  virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
+
+  virtual void SetFromRaw() = 0;
+
+  virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
+
   uint16_t sp_reg() { return sp_reg_; }
   uint16_t total_regs() { return total_regs_; }
 
-  static Regs* RemoteGet(pid_t pid, uint32_t* machine_type);
+  static uint32_t CurrentMachineType();
+  static Regs* RemoteGet(pid_t pid);
+  static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext);
+  static Regs* CreateFromLocal();
 
  protected:
   uint16_t total_regs_;
@@ -72,10 +84,6 @@
       : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
   virtual ~RegsImpl() = default;
 
-  uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override;
-
-  bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;
-
   uint64_t pc() override { return pc_; }
   uint64_t sp() override { return sp_; }
 
@@ -97,7 +105,15 @@
   RegsArm();
   virtual ~RegsArm() = default;
 
+  virtual uint32_t MachineType() 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;
 };
 
 class RegsArm64 : public RegsImpl<uint64_t> {
@@ -105,7 +121,15 @@
   RegsArm64();
   virtual ~RegsArm64() = default;
 
+  virtual uint32_t MachineType() 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;
 };
 
 class RegsX86 : public RegsImpl<uint32_t> {
@@ -113,7 +137,17 @@
   RegsX86();
   virtual ~RegsX86() = default;
 
+  virtual uint32_t MachineType() 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;
+
+  void SetFromUcontext(x86_ucontext_t* ucontext);
 };
 
 class RegsX86_64 : public RegsImpl<uint64_t> {
@@ -121,7 +155,19 @@
   RegsX86_64();
   virtual ~RegsX86_64() = default;
 
+  virtual uint32_t MachineType() 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;
+
+  void SetFromUcontext(x86_64_ucontext_t* ucontext);
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
new file mode 100644
index 0000000..d1461d8
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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_REGS_GET_LOCAL_H
+#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+
+namespace unwindstack {
+
+#if defined(__arm__)
+
+inline void RegsGetLocal(Regs* regs) {
+  void* reg_data = regs->RawData();
+  asm volatile(
+      ".align 2\n"
+      "bx pc\n"
+      "nop\n"
+      ".code 32\n"
+      "stmia %[base], {r0-r12}\n"
+      "add %[base], #52\n"
+      "mov r1, r13\n"
+      "mov r2, r14\n"
+      "mov r3, r15\n"
+      "stmia %[base], {r1-r3}\n"
+      "orr %[base], pc, #1\n"
+      "bx %[base]\n"
+      : [base] "+r"(reg_data)
+      :
+      : "memory");
+
+  regs->SetFromRaw();
+}
+
+#elif defined(__aarch64__)
+
+inline void RegsGetLocal(Regs* regs) {
+  void* reg_data = regs->RawData();
+  asm volatile(
+      "1:\n"
+      "stp x0, x1, [%[base], #0]\n"
+      "stp x2, x3, [%[base], #16]\n"
+      "stp x4, x5, [%[base], #32]\n"
+      "stp x6, x7, [%[base], #48]\n"
+      "stp x8, x9, [%[base], #64]\n"
+      "stp x10, x11, [%[base], #80]\n"
+      "stp x12, x13, [%[base], #96]\n"
+      "stp x14, x15, [%[base], #112]\n"
+      "stp x16, x17, [%[base], #128]\n"
+      "stp x18, x19, [%[base], #144]\n"
+      "stp x20, x21, [%[base], #160]\n"
+      "stp x22, x23, [%[base], #176]\n"
+      "stp x24, x25, [%[base], #192]\n"
+      "stp x26, x27, [%[base], #208]\n"
+      "stp x28, x29, [%[base], #224]\n"
+      "str x30, [%[base], #240]\n"
+      "mov x12, sp\n"
+      "adr x13, 1b\n"
+      "stp x12, x13, [%[base], #248]\n"
+      : [base] "+r"(reg_data)
+      :
+      : "x12", "x13", "memory");
+
+  regs->SetFromRaw();
+}
+
+#elif defined(__i386__) || defined(__x86_64__)
+
+extern "C" void AsmGetRegs(void* regs);
+
+inline void RegsGetLocal(Regs* regs) {
+  AsmGetRegs(regs->RawData());
+
+  regs->SetFromRaw();
+}
+
+#elif defined(__mips__)
+
+// Stub to allow mips to build.
+void RegsGetLocal(Regs*) {}
+
+#endif
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
new file mode 100644
index 0000000..71703b4
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -0,0 +1,84 @@
+/*
+ * 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_UNWINDER_H
+#define _LIBUNWINDSTACK_UNWINDER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+
+struct FrameData {
+  size_t num;
+
+  uint64_t rel_pc;
+  uint64_t pc;
+  uint64_t sp;
+
+  std::string function_name;
+  uint64_t function_offset;
+
+  std::string map_name;
+  uint64_t map_offset;
+  uint64_t map_start;
+  uint64_t map_end;
+  uint64_t map_load_bias;
+  int map_flags;
+};
+
+class Unwinder {
+ public:
+  Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
+      : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+    frames_.reserve(max_frames);
+  }
+  ~Unwinder() = default;
+
+  void Unwind(std::set<std::string>* initial_map_names_to_skip = nullptr);
+
+  size_t NumFrames() { return frames_.size(); }
+
+  const std::vector<FrameData>& frames() { return frames_; }
+
+  std::string FormatFrame(size_t frame_num);
+  static std::string FormatFrame(const FrameData& frame, bool bits32);
+
+ private:
+  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
+
+  size_t max_frames_;
+  Maps* maps_;
+  Regs* regs_;
+  std::vector<FrameData> frames_;
+  std::shared_ptr<Memory> process_memory_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 4fff48e..94cb493 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -23,13 +23,16 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
 #include "ArmExidx.h"
-#include "Regs.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
  protected:
   void Init(Memory* process_memory = nullptr) {
@@ -1092,3 +1095,5 @@
 }
 
 INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index aed75bf..caad131 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -21,12 +21,15 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Log.h>
+
 #include "ArmExidx.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ArmExidxExtractTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -329,3 +332,5 @@
   ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 967c2c2..b17ca33 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -23,15 +23,18 @@
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfCfaLogTest : public ::testing::Test {
  protected:
@@ -812,3 +815,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 687af7d..73a67ac 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -21,15 +21,19 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
+#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfCfaTest : public ::testing::Test {
  protected:
@@ -957,3 +961,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 0000000..90baabe
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
+ public:
+  MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
+  ~MockDwarfDebugFrame() = default;
+
+  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+  void TestSetOffset(uint64_t offset) { this->offset_ = offset; }
+  void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; }
+  void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
+    this->fdes_.push_back(info);
+  }
+
+  uint64_t TestGetFdeCount() { return this->fde_count_; }
+  uint8_t TestGetOffset() { return this->offset_; }
+  uint8_t TestGetEndOffset() { return this->end_offset_; }
+  void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
+    *info = this->fdes_[index];
+  }
+};
+
+template <typename TypeParam>
+class DwarfDebugFrameTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete debug_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfDebugFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+
+  this->memory_.SetData32(0x5200, 0xfc);
+  this->memory_.SetData32(0x5204, 0);
+  this->memory_.SetData32(0x5208, 0x2500);
+  this->memory_.SetData32(0x520c, 0x300);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x5300, 0xfc);
+  this->memory_.SetData32(0x5304, 0xffffffff);
+  this->memory_.SetData8(0x5308, 1);
+  this->memory_.SetData8(0x5309, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5400, 0xfc);
+  this->memory_.SetData32(0x5404, 0x300);
+  this->memory_.SetData32(0x5408, 0x3500);
+  this->memory_.SetData32(0x540c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xfc);
+  this->memory_.SetData32(0x5504, 0x300);
+  this->memory_.SetData32(0x5508, 0x4500);
+  this->memory_.SetData32(0x550c, 0x500);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x2500U, info.start);
+  EXPECT_EQ(0x2800U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x3500U, info.start);
+  EXPECT_EQ(0x3900U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x4500U, info.start);
+  EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  this->memory_.SetData8(0x5009, '\0');
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x1000);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
+
+  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
+
+  this->memory_.SetData32(0x5200, 0xffffffff);
+  this->memory_.SetData64(0x5204, 0xf4);
+  this->memory_.SetData64(0x520c, 0);
+  this->memory_.SetData64(0x5214, 0x2500);
+  this->memory_.SetData64(0x521c, 0x300);
+
+  // CIE 64 information.
+  this->memory_.SetData32(0x5300, 0xffffffff);
+  this->memory_.SetData64(0x5304, 0xf4);
+  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5314, 1);
+  this->memory_.SetData8(0x5315, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5400, 0xffffffff);
+  this->memory_.SetData64(0x5404, 0xf4);
+  this->memory_.SetData64(0x540c, 0x300);
+  this->memory_.SetData64(0x5414, 0x3500);
+  this->memory_.SetData64(0x541c, 0x400);
+
+  this->memory_.SetData32(0x5500, 0xffffffff);
+  this->memory_.SetData64(0x5504, 0xf4);
+  this->memory_.SetData64(0x550c, 0x300);
+  this->memory_.SetData64(0x5514, 0x4500);
+  this->memory_.SetData64(0x551c, 0x500);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(1, &info);
+  EXPECT_EQ(0x5200U, info.offset);
+  EXPECT_EQ(0x2500U, info.start);
+  EXPECT_EQ(0x2800U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(2, &info);
+  EXPECT_EQ(0x5400U, info.offset);
+  EXPECT_EQ(0x3500U, info.start);
+  EXPECT_EQ(0x3900U, info.end);
+
+  this->debug_frame_->TestGetFdeInfo(3, &info);
+  EXPECT_EQ(0x5500U, info.offset);
+  EXPECT_EQ(0x4500U, info.start);
+  EXPECT_EQ(0x4a00U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xf4);
+  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x5014, 1);
+  this->memory_.SetData8(0x5015, '\0');
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xf4);
+  this->memory_.SetData64(0x510c, 0x1000);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
+
+  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 1);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
+  // Code alignment factor.
+  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetData8(0x5014, 0x84);
+  // Augmentation length
+  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
+  // R data.
+  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
+  // CIE 32 information.
+  this->memory_.SetData32(0x5000, 0xfc);
+  this->memory_.SetData32(0x5004, 0xffffffff);
+  this->memory_.SetData8(0x5008, 4);
+  // Augment string.
+  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
+  // Address size.
+  this->memory_.SetData8(0x500e, 4);
+  // Segment size.
+  this->memory_.SetData8(0x500f, 0);
+  // Code alignment factor.
+  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
+  // Data alignment factor.
+  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
+  // Return address register
+  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
+  // Augmentation length
+  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
+  // L data.
+  this->memory_.SetData8(0x501a, 0x10);
+  // P data.
+  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
+  this->memory_.SetData32(0x501c, 0x100);
+  // R data.
+  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0);
+  this->memory_.SetData16(0x5108, 0x1500);
+  this->memory_.SetData16(0x510a, 0x200);
+
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  this->debug_frame_->TestGetFdeInfo(0, &info);
+  EXPECT_EQ(0x5100U, info.offset);
+  EXPECT_EQ(0x1500U, info.start);
+  EXPECT_EQ(0x1700U, info.end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
+  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+  for (size_t i = 0; i < 9; i++) {
+    info.start = 0x1000 * (i + 1);
+    info.end = 0x1000 * (i + 2) - 0x10;
+    info.offset = 0x5000 + i * 0x20;
+    this->debug_frame_->TestPushFdeInfo(info);
+  }
+
+  this->debug_frame_->TestSetFdeCount(0);
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+
+  this->debug_frame_->TestSetFdeCount(9);
+  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  // Odd number of elements.
+  for (size_t i = 0; i < 9; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+                                                                             << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  }
+
+  // Even number of elements.
+  this->debug_frame_->TestSetFdeCount(10);
+  info.start = 0xa000;
+  info.end = 0xaff0;
+  info.offset = 0x5120;
+  this->debug_frame_->TestPushFdeInfo(info);
+
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
+                                                                             << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
+        << "Failed at index " << i;
+    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  }
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
+  this->debug_frame_->TestSetOffset(0x4000);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0xf000, 0x100);
+  this->memory_.SetData32(0xf004, 0xffffffff);
+  this->memory_.SetData8(0xf008, 0x1);
+  this->memory_.SetData8(0xf009, '\0');
+  this->memory_.SetData8(0xf00a, 4);
+  this->memory_.SetData8(0xf00b, 8);
+  this->memory_.SetData8(0xf00c, 0x20);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x14000, 0x20);
+  this->memory_.SetData32(0x14004, 0xb000);
+  this->memory_.SetData32(0x14008, 0x9000);
+  this->memory_.SetData32(0x1400c, 0x100);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x9000U, fde->pc_start);
+  EXPECT_EQ(0x9100U, fde->pc_end);
+  EXPECT_EQ(0xf000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
+  this->debug_frame_->TestSetOffset(0x2000);
+
+  // CIE 64 information.
+  this->memory_.SetData32(0x6000, 0xffffffff);
+  this->memory_.SetData64(0x6004, 0x100);
+  this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
+  this->memory_.SetData8(0x6014, 0x1);
+  this->memory_.SetData8(0x6015, '\0');
+  this->memory_.SetData8(0x6016, 4);
+  this->memory_.SetData8(0x6017, 8);
+  this->memory_.SetData8(0x6018, 0x20);
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x8000, 0xffffffff);
+  this->memory_.SetData64(0x8004, 0x200);
+  this->memory_.SetData64(0x800c, 0x4000);
+  this->memory_.SetData64(0x8014, 0x5000);
+  this->memory_.SetData64(0x801c, 0x300);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x5000U, fde->pc_start);
+  EXPECT_EQ(0x5300U, fde->pc_end);
+  EXPECT_EQ(0x6000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64,
+                           Init64_fde_not_following_cie, Init_version1, Init_version4,
+                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 0000000..21114da
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
+ public:
+  MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
+  ~MockDwarfEhFrame() = default;
+
+  void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+  void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+  void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
+    this->fde_info_[index] = info;
+  }
+
+  uint8_t TestGetVersion() { return this->version_; }
+  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
+  uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+  uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+  uint64_t TestGetFdeCount() { return this->fde_count_; }
+  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete eh_frame_; }
+
+  MemoryFake memory_;
+  MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameTest, Init) {
+  this->memory_.SetMemory(
+      0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 126);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+  EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+  // Verify an unexpected version will cause a fail.
+  this->memory_.SetData8(0x1000, 0);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+  this->memory_.SetData8(0x1000, 2);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x1384U, info->pc);
+  EXPECT_EQ(0x1540U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x3344U, info->pc);
+  EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+
+  // Clear the memory so that this will fail if it doesn't read cached data.
+  this->memory_.Clear();
+
+  info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x344U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info;
+  for (size_t i = 0; i < 10; i++) {
+    info.pc = 0x1000 * (i + 1);
+    info.offset = 0x5000 + i * 0x20;
+    this->eh_frame_->TestSetFdeInfo(i, info);
+  }
+
+  uint64_t fde_offset;
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+  // Not an error, just not found.
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+  // Even number of elements.
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index "
+                                                                              << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+  // Odd number of elements.
+  for (size_t i = 0; i < 9; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index "
+                                                                             << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  // Verify that if entries is zero, that it fails.
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+  EXPECT_EQ(0x500U, fde_offset);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+
+  // Expect that the data is cached so no more memory reads will occur.
+  this->memory_.Clear();
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x1048);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) {
+  this->eh_frame_->TestSetFdeCount(0);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info;
+  info.pc = 0x550;
+  info.offset = 0x10500;
+  this->eh_frame_->TestSetFdeInfo(5, info);
+  info.pc = 0x750;
+  info.offset = 0x10700;
+  this->eh_frame_->TestSetFdeInfo(7, info);
+  info.pc = 0x850;
+  info.offset = 0x10800;
+  this->eh_frame_->TestSetFdeInfo(8, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+  EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetTableEntrySize(0);
+
+  typename DwarfEhFrame<TypeParam>::FdeInfo info;
+  info.pc = 0x50;
+  info.offset = 0x10000;
+  this->eh_frame_->TestSetFdeInfo(0, info);
+  info.pc = 0x150;
+  info.offset = 0x10100;
+  this->eh_frame_->TestSetFdeInfo(1, info);
+  info.pc = 0x250;
+  info.offset = 0x10200;
+  this->eh_frame_->TestSetFdeInfo(2, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
+  EXPECT_EQ(0x10100U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
+  // CIE 32 information.
+  this->memory_.SetData32(0xf000, 0x100);
+  this->memory_.SetData32(0xf004, 0);
+  this->memory_.SetData8(0xf008, 0x1);
+  this->memory_.SetData8(0xf009, '\0');
+  this->memory_.SetData8(0xf00a, 4);
+  this->memory_.SetData8(0xf00b, 8);
+  this->memory_.SetData8(0xf00c, 0x20);
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x14000, 0x20);
+  this->memory_.SetData32(0x14004, 0x5004);
+  this->memory_.SetData32(0x14008, 0x9000);
+  this->memory_.SetData32(0x1400c, 0x100);
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x1d00cU, fde->pc_start);
+  EXPECT_EQ(0x1d10cU, fde->pc_end);
+  EXPECT_EQ(0xf000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x6000, 0xffffffff);
+  this->memory_.SetData64(0x6004, 0x100);
+  this->memory_.SetData64(0x600c, 0);
+  this->memory_.SetData8(0x6014, 0x1);
+  this->memory_.SetData8(0x6015, '\0');
+  this->memory_.SetData8(0x6016, 4);
+  this->memory_.SetData8(0x6017, 8);
+  this->memory_.SetData8(0x6018, 0x20);
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x8000, 0xffffffff);
+  this->memory_.SetData64(0x8004, 0x200);
+  this->memory_.SetData64(0x800c, 0x200c);
+  this->memory_.SetData64(0x8014, 0x5000);
+  this->memory_.SetData64(0x801c, 0x300);
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0xd01cU, fde->pc_start);
+  EXPECT_EQ(0xd31cU, fde->pc_end);
+  EXPECT_EQ(0x6000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
+                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
+                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
+                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+                           GetCieFde32, GetCieFde64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 4877f36..f12d2fe 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -21,10 +21,12 @@
 
 #include <gtest/gtest.h>
 
-#include "DwarfMemory.h"
+#include <unwindstack/DwarfMemory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class DwarfMemoryTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -50,6 +52,8 @@
   void ReadEncodedValue_non_zero_adjust();
   template <typename AddressType>
   void ReadEncodedValue_overflow();
+  template <typename AddressType>
+  void ReadEncodedValue_high_bit_set();
 
   MemoryFake memory_;
   std::unique_ptr<DwarfMemory> dwarf_mem_;
@@ -237,9 +241,13 @@
   ASSERT_EQ(0U, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) {
+  ReadEncodedValue_omit<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) {
+  ReadEncodedValue_omit<uint64_t>();
+}
 
 TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
   uint64_t value = 100;
@@ -302,9 +310,13 @@
   ASSERT_EQ(0xffffffffffffe100ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) {
+  ReadEncodedValue_leb128<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) {
+  ReadEncodedValue_leb128<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data1() {
@@ -319,9 +331,13 @@
   ASSERT_EQ(0xffffffffffffffe0ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) {
+  ReadEncodedValue_data1<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) {
+  ReadEncodedValue_data1<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data2() {
@@ -336,9 +352,13 @@
   ASSERT_EQ(0xffffffffffffe000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) {
+  ReadEncodedValue_data2<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) {
+  ReadEncodedValue_data2<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data4() {
@@ -353,9 +373,13 @@
   ASSERT_EQ(0xffffffffe0000000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) {
+  ReadEncodedValue_data4<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) {
+  ReadEncodedValue_data4<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data8() {
@@ -370,9 +394,13 @@
   ASSERT_EQ(0xe000000000000000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) {
+  ReadEncodedValue_data8<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) {
+  ReadEncodedValue_data8<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
@@ -409,6 +437,26 @@
   ReadEncodedValue_overflow<uint64_t>();
 }
 
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_high_bit_set() {
+  uint64_t value;
+  memory_.SetData32(0, 0x15234);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+
+  dwarf_mem_->set_func_offset(0x60000);
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+  ASSERT_EQ(0x75234U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint32_t) {
+  ReadEncodedValue_high_bit_set<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint64_t) {
+  ReadEncodedValue_high_bit_set<uint64_t>();
+}
+
 TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
   uint64_t value = 0x1234;
   ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
@@ -470,3 +518,5 @@
   ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
   ASSERT_EQ(0x14234U, value);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index d18aad0..234d1c9 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -21,14 +21,17 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfError.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "Log.h"
-#include "Regs.h"
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfOpLogTest : public ::testing::Test {
  protected:
@@ -66,3 +69,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 20c488a..2d5007b 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -21,14 +21,17 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfError.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "Log.h"
 
 #include "MemoryFake.h"
 #include "RegsFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfOpTest : public ::testing::Test {
  protected:
@@ -1483,7 +1486,7 @@
   }
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(32, 10);
+  RegsImplFake<TypeParam> regs(32, 10);
   for (size_t i = 0; i < 32; i++) {
     regs[i] = i + 10;
   }
@@ -1515,7 +1518,7 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(16, 10);
+  RegsImplFake<TypeParam> regs(16, 10);
   for (size_t i = 0; i < 16; i++) {
     regs[i] = i + 10;
   }
@@ -1541,7 +1544,7 @@
                                         0x92, 0x80, 0x15, 0x80, 0x02};
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(10, 10);
+  RegsImplFake<TypeParam> regs(10, 10);
   regs[5] = 0x45;
   regs[6] = 0x190;
   this->op_->set_regs(&regs);
@@ -1579,3 +1582,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 71b114a..c701a29 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -19,12 +19,17 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "DwarfSection.h"
+#include <unwindstack/DwarfSection.h>
+
+#include "DwarfEncoding.h"
+#include "DwarfError.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 #include "RegsFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
  public:
@@ -85,7 +90,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -93,13 +98,14 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -108,13 +114,14 @@
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -125,14 +132,16 @@
   TypeParam cfa_value = 0x12345;
   this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x12345U, regs.sp());
   EXPECT_EQ(0x20U, regs.pc());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -141,14 +150,16 @@
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  ASSERT_FALSE(finished);
   EXPECT_EQ(0x80000000U, regs.sp());
   EXPECT_EQ(0x20U, regs.pc());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -157,59 +168,63 @@
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
   DwarfCie cie{.return_address_register = 60};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -217,14 +232,16 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
   EXPECT_EQ(0x2000U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -233,14 +250,16 @@
   regs[6] = 0x4000;
   regs[9] = 0x3000;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
   EXPECT_EQ(0x4000U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -249,13 +268,14 @@
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
   loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -263,13 +283,14 @@
   regs[8] = 0x10;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   if (sizeof(TypeParam) == sizeof(uint64_t)) {
@@ -287,7 +308,9 @@
   loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
   loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
   loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x10U, regs.pc());
   EXPECT_EQ(0x2100U, regs.sp());
   EXPECT_EQ(0x2200U, regs[1]);
@@ -301,7 +324,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -310,14 +333,16 @@
   regs[8] = 0x10;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_TRUE(finished);
   EXPECT_EQ(0U, regs.pc());
   EXPECT_EQ(0x10U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -325,14 +350,16 @@
   regs[5] = 0x20;
   regs[8] = 0x10;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
   EXPECT_EQ(0x10U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -342,14 +369,16 @@
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   // This should not result in any errors.
   loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
   EXPECT_EQ(0x10U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -360,14 +389,16 @@
   this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x3000U, regs.sp());
   EXPECT_EQ(0x12345U, regs.pc());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -376,14 +407,16 @@
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x3000U, regs.sp());
   EXPECT_EQ(0x80000000U, regs.pc());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10, 9);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -391,7 +424,8 @@
   regs[5] = 0x100;
   regs[8] = 0x2000;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
   EXPECT_EQ(0x2000U, regs.sp());
   EXPECT_EQ(0x100U, regs.pc());
 }
@@ -830,3 +864,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 20f0e2a..3fcd2b6 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -19,10 +19,12 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "DwarfSection.h"
+#include <unwindstack/DwarfSection.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class MockDwarfSection : public DwarfSection {
  public:
   MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
@@ -30,7 +32,7 @@
 
   MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
 
-  MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+  MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
 
   MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
 
@@ -102,7 +104,8 @@
   EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
       .WillOnce(::testing::Return(false));
 
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cie_null) {
@@ -116,7 +119,8 @@
       .WillOnce(::testing::Return(true));
   EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
 
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
@@ -134,7 +138,8 @@
   EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Return(false));
 
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_pass) {
@@ -153,8 +158,11 @@
       .WillOnce(::testing::Return(true));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillOnce(::testing::Return(true));
 
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+  bool finished;
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
new file mode 100644
index 0000000..71f7f6b
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+std::deque<FunctionData> ElfInterfaceFake::functions_;
+std::deque<StepData> ElfInterfaceFake::steps_;
+
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
+  if (functions_.empty()) {
+    return false;
+  }
+  auto entry = functions_.front();
+  functions_.pop_front();
+  *name = entry.name;
+  *offset = entry.offset;
+  return true;
+}
+
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
+  if (steps_.empty()) {
+    return false;
+  }
+  auto entry = steps_.front();
+  steps_.pop_front();
+
+  if (entry.pc == 0 && entry.sp == 0 && !entry.finished) {
+    // Pretend as though there is no frame.
+    return false;
+  }
+
+  RegsFake* fake_regs = reinterpret_cast<RegsFake*>(regs);
+  fake_regs->FakeSetPc(entry.pc);
+  fake_regs->FakeSetSp(entry.sp);
+  *finished = entry.finished;
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
new file mode 100644
index 0000000..4359bca
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.h
@@ -0,0 +1,84 @@
+/*
+ * 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_TESTS_ELF_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+struct StepData {
+  StepData(uint64_t pc, uint64_t sp, bool finished) : pc(pc), sp(sp), finished(finished) {}
+  uint64_t pc;
+  uint64_t sp;
+  bool finished;
+};
+
+struct FunctionData {
+  FunctionData(std::string name, uint64_t offset) : name(name), offset(offset) {}
+
+  std::string name;
+  uint64_t offset;
+};
+
+class ElfFake : public Elf {
+ public:
+  ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
+  virtual ~ElfFake() = default;
+
+  void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+};
+
+class ElfInterfaceFake : public ElfInterface {
+ public:
+  ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
+  virtual ~ElfInterfaceFake() = default;
+
+  bool Init() override { return false; }
+  void InitHeaders() override {}
+  bool GetSoname(std::string*) override { return false; }
+
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
+
+  bool Step(uint64_t, Regs*, Memory*, bool*) override;
+
+  void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+
+  static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
+  static void FakePushStepData(const StepData data) { steps_.push_back(data); }
+
+  static void FakeClear() {
+    functions_.clear();
+    steps_.clear();
+  }
+
+ private:
+  static std::deque<FunctionData> functions_;
+  static std::deque<StepData> steps_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 67c9a6b..4df7e1c 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,12 +20,15 @@
 
 #include <vector>
 
+#include <unwindstack/Regs.h>
+
 #include "ElfInterfaceArm.h"
 #include "Machine.h"
-#include "Regs.h"
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ElfInterfaceArmTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -319,7 +322,8 @@
   ElfInterfaceArm interface(&memory_);
 
   // FindEntry fails.
-  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
 
   // ExtractEntry should fail.
   interface.set_start_offset(0x1000);
@@ -332,15 +336,16 @@
   regs[ARM_REG_LR] = 0x20000;
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
-  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
 
   // Eval should fail.
   memory_.SetData32(0x1004, 0x81000000);
-  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
 
   // Everything should pass.
   memory_.SetData32(0x1004, 0x80b0b0b0);
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(finished);
   ASSERT_EQ(0x1000U, regs.sp());
   ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
   ASSERT_EQ(0x20000U, regs.pc());
@@ -364,9 +369,57 @@
   regs.set_pc(0x1234);
 
   // Everything should pass.
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(finished);
   ASSERT_EQ(0x10004U, regs.sp());
   ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
   ASSERT_EQ(0x10U, regs.pc());
   ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
 }
+
+TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) {
+  ElfInterfaceArm interface(&memory_);
+
+  interface.set_start_offset(0x1000);
+  interface.set_total_entries(1);
+  memory_.SetData32(0x1000, 0x6000);
+  memory_.SetData32(0x1004, 1);
+
+  RegsArm regs;
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0x10000U, regs.sp());
+  ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+  ASSERT_EQ(0x1234U, regs.pc());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) {
+  ElfInterfaceArm interface(&memory_);
+
+  interface.set_start_offset(0x1000);
+  interface.set_total_entries(1);
+  memory_.SetData32(0x1000, 0x6000);
+  memory_.SetData32(0x1004, 0x808000b0);
+
+  RegsArm regs;
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0x10000U, regs.sp());
+  ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+  ASSERT_EQ(0x1234U, regs.pc());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index c31903d..acb7320 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -20,7 +20,9 @@
 
 #include <gtest/gtest.h>
 
-#include "ElfInterface.h"
+#include <unwindstack/ElfInterface.h>
+
+#include "DwarfEncoding.h"
 #include "ElfInterfaceArm.h"
 
 #include "MemoryFake.h"
@@ -33,6 +35,8 @@
 #define EM_AARCH64 183
 #endif
 
+namespace unwindstack {
+
 class ElfInterfaceTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -67,9 +71,49 @@
   template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
   void SonameSize();
 
+  template <typename ElfType>
+  void InitHeadersEhFrameTest();
+
+  template <typename ElfType>
+  void InitHeadersDebugFrame();
+
+  template <typename ElfType>
+  void InitHeadersEhFrameFail();
+
+  template <typename ElfType>
+  void InitHeadersDebugFrameFail();
+
+  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+  void InitSectionHeadersMalformed();
+
+  template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+  void InitSectionHeaders(uint64_t entry_size);
+
+  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+  void InitSectionHeadersOffsets();
+
+  template <typename Sym>
+  void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+               uint64_t sym_offset, const char* name);
+
   MemoryFake memory_;
 };
 
+template <typename Sym>
+void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+                               uint64_t sym_offset, const char* name) {
+  Sym sym;
+  memset(&sym, 0, sizeof(sym));
+  sym.st_info = STT_FUNC;
+  sym.st_value = value;
+  sym.st_size = size;
+  sym.st_name = name_offset;
+  sym.st_shndx = SHN_COMMON;
+
+  memory_.SetMemory(offset, &sym, sizeof(sym));
+  memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1);
+}
+
 template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
 void ElfInterfaceTest::SinglePtLoad() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
@@ -571,3 +615,315 @@
 TEST_F(ElfInterfaceTest, elf64_soname_size) {
   SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
+
+class MockElfInterface32 : public ElfInterface32 {
+ public:
+  MockElfInterface32(Memory* memory) : ElfInterface32(memory) {}
+  virtual ~MockElfInterface32() = default;
+
+  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class MockElfInterface64 : public ElfInterface64 {
+ public:
+  MockElfInterface64(Memory* memory) : ElfInterface64(memory) {}
+  virtual ~MockElfInterface64() = default;
+
+  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+
+  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameTest() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0x10000);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0);
+  elf.TestSetDebugFrameSize(0);
+
+  memory_.SetMemory(0x10000,
+                    std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
+  memory_.SetData32(0x10004, 0x500);
+  memory_.SetData32(0x10008, 250);
+
+  elf.InitHeaders();
+
+  EXPECT_FALSE(elf.eh_frame() == nullptr);
+  EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
+  InitHeadersEhFrameTest<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
+  InitHeadersEhFrameTest<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrame() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0x5000);
+  elf.TestSetDebugFrameSize(0x200);
+
+  memory_.SetData32(0x5000, 0xfc);
+  memory_.SetData32(0x5004, 0xffffffff);
+  memory_.SetData8(0x5008, 1);
+  memory_.SetData8(0x5009, '\0');
+
+  memory_.SetData32(0x5100, 0xfc);
+  memory_.SetData32(0x5104, 0);
+  memory_.SetData32(0x5108, 0x1500);
+  memory_.SetData32(0x510c, 0x200);
+
+  elf.InitHeaders();
+
+  EXPECT_TRUE(elf.eh_frame() == nullptr);
+  EXPECT_FALSE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
+  InitHeadersDebugFrame<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
+  InitHeadersDebugFrame<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameFail() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0x1000);
+  elf.TestSetEhFrameSize(0x100);
+  elf.TestSetDebugFrameOffset(0);
+  elf.TestSetDebugFrameSize(0);
+
+  elf.InitHeaders();
+
+  EXPECT_TRUE(elf.eh_frame() == nullptr);
+  EXPECT_EQ(0U, elf.eh_frame_offset());
+  EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size());
+  EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
+  InitHeadersEhFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
+  InitHeadersEhFrameFail<MockElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrameFail() {
+  ElfType elf(&memory_);
+
+  elf.TestSetEhFrameOffset(0);
+  elf.TestSetEhFrameSize(0);
+  elf.TestSetDebugFrameOffset(0x1000);
+  elf.TestSetDebugFrameSize(0x100);
+
+  elf.InitHeaders();
+
+  EXPECT_TRUE(elf.eh_frame() == nullptr);
+  EXPECT_TRUE(elf.debug_frame() == nullptr);
+  EXPECT_EQ(0U, elf.debug_frame_offset());
+  EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size());
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
+  InitHeadersDebugFrameFail<MockElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
+  InitHeadersDebugFrameFail<MockElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformed() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = 0x1000;
+  ehdr.e_shnum = 10;
+  ehdr.e_shentsize = sizeof(Shdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  ASSERT_TRUE(elf->Init());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
+  InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
+  InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x1000;
+
+  Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 10;
+  ehdr.e_shentsize = entry_size;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_SYMTAB;
+  shdr.sh_link = 4;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = sizeof(Sym);
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_DYNSYM;
+  shdr.sh_link = 4;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = sizeof(Sym);
+  shdr.sh_size = shdr.sh_entsize * 10;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0xa000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for the entries.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
+  InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
+
+  ASSERT_TRUE(elf->Init());
+  EXPECT_EQ(0U, elf->debug_frame_offset());
+  EXPECT_EQ(0U, elf->debug_frame_size());
+  EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+  EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+  // Look in the first symbol table.
+  std::string name;
+  uint64_t name_offset;
+  ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
+  EXPECT_EQ("function_one", name);
+  EXPECT_EQ(16U, name_offset);
+  ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
+  EXPECT_EQ("function_two", name);
+  EXPECT_EQ(32U, name_offset);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers32) {
+  InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers64) {
+  InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
+  InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
+  InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsets() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 10;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x200;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x800;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x100;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0x6000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x500;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+  memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+
+  ASSERT_TRUE(elf->Init());
+  EXPECT_EQ(0x6000U, elf->debug_frame_offset());
+  EXPECT_EQ(0x500U, elf->debug_frame_size());
+  EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
+  EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
+  InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
+  InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 25fec8e..42a0246 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -15,20 +15,25 @@
  */
 
 #include <elf.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <gtest/gtest.h>
 
-#include "Elf.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
 
+#include "ElfTestUtils.h"
+#include "LogFake.h"
 #include "MemoryFake.h"
 
 #if !defined(PT_ARM_EXIDX)
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-#if !defined(EM_AARCH64)
-#define EM_AARCH64 183
-#endif
+namespace unwindstack {
 
 class ElfTest : public ::testing::Test {
  protected:
@@ -36,35 +41,16 @@
     memory_ = new MemoryFake;
   }
 
-  template <typename Ehdr>
-  void InitEhdr(Ehdr* ehdr) {
-    memset(ehdr, 0, sizeof(Ehdr));
-    memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
-    ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
-    ehdr->e_ident[EI_VERSION] = EV_CURRENT;
-    ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
-  }
-
-  void InitElf32(uint32_t type) {
+  void InitElf32(uint32_t machine_type) {
     Elf32_Ehdr ehdr;
+    TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, machine_type);
 
-    InitEhdr<Elf32_Ehdr>(&ehdr);
-    ehdr.e_ident[EI_CLASS] = ELFCLASS32;
-
-    ehdr.e_type = ET_DYN;
-    ehdr.e_machine = type;
-    ehdr.e_version = EV_CURRENT;
-    ehdr.e_entry = 0;
     ehdr.e_phoff = 0x100;
-    ehdr.e_shoff = 0;
-    ehdr.e_flags = 0;
     ehdr.e_ehsize = sizeof(ehdr);
     ehdr.e_phentsize = sizeof(Elf32_Phdr);
     ehdr.e_phnum = 1;
     ehdr.e_shentsize = sizeof(Elf32_Shdr);
-    ehdr.e_shnum = 0;
-    ehdr.e_shstrndx = 0;
-    if (type == EM_ARM) {
+    if (machine_type == EM_ARM) {
       ehdr.e_flags = 0x5000200;
       ehdr.e_phnum = 2;
     }
@@ -73,16 +59,13 @@
     Elf32_Phdr phdr;
     memset(&phdr, 0, sizeof(phdr));
     phdr.p_type = PT_LOAD;
-    phdr.p_offset = 0;
-    phdr.p_vaddr = 0;
-    phdr.p_paddr = 0;
     phdr.p_filesz = 0x10000;
     phdr.p_memsz = 0x10000;
     phdr.p_flags = PF_R | PF_X;
     phdr.p_align = 0x1000;
     memory_->SetMemory(0x100, &phdr, sizeof(phdr));
 
-    if (type == EM_ARM) {
+    if (machine_type == EM_ARM) {
       memset(&phdr, 0, sizeof(phdr));
       phdr.p_type = PT_ARM_EXIDX;
       phdr.p_offset = 0x30000;
@@ -96,33 +79,21 @@
     }
   }
 
-  void InitElf64(uint32_t type) {
+  void InitElf64(uint32_t machine_type) {
     Elf64_Ehdr ehdr;
+    TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, machine_type);
 
-    InitEhdr<Elf64_Ehdr>(&ehdr);
-    ehdr.e_ident[EI_CLASS] = ELFCLASS64;
-
-    ehdr.e_type = ET_DYN;
-    ehdr.e_machine = type;
-    ehdr.e_version = EV_CURRENT;
-    ehdr.e_entry = 0;
     ehdr.e_phoff = 0x100;
-    ehdr.e_shoff = 0;
     ehdr.e_flags = 0x5000200;
     ehdr.e_ehsize = sizeof(ehdr);
     ehdr.e_phentsize = sizeof(Elf64_Phdr);
     ehdr.e_phnum = 1;
     ehdr.e_shentsize = sizeof(Elf64_Shdr);
-    ehdr.e_shnum = 0;
-    ehdr.e_shstrndx = 0;
     memory_->SetMemory(0, &ehdr, sizeof(ehdr));
 
     Elf64_Phdr phdr;
     memset(&phdr, 0, sizeof(phdr));
     phdr.p_type = PT_LOAD;
-    phdr.p_offset = 0;
-    phdr.p_vaddr = 0;
-    phdr.p_paddr = 0;
     phdr.p_filesz = 0x10000;
     phdr.p_memsz = 0x10000;
     phdr.p_flags = PF_R | PF_X;
@@ -158,7 +129,34 @@
   uint64_t func_offset;
   ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
 
-  ASSERT_FALSE(elf.Step(0, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
+}
+
+TEST_F(ElfTest, elf32_invalid_machine) {
+  Elf elf(memory_);
+
+  InitElf32(EM_PPC);
+
+  ResetLogs();
+  ASSERT_FALSE(elf.Init());
+
+  ASSERT_EQ("", GetFakeLogBuf());
+  ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n",
+            GetFakeLogPrint());
+}
+
+TEST_F(ElfTest, elf64_invalid_machine) {
+  Elf elf(memory_);
+
+  InitElf64(EM_PPC64);
+
+  ResetLogs();
+  ASSERT_FALSE(elf.Init());
+
+  ASSERT_EQ("", GetFakeLogBuf());
+  ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n",
+            GetFakeLogPrint());
 }
 
 TEST_F(ElfTest, elf_arm) {
@@ -208,3 +206,95 @@
   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) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init_fail64) {
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init32) {
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size());
+
+  elf.InitGnuDebugdata();
+  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init64) {
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(offset, ptr, size);
+                                               });
+
+  Elf elf(memory_);
+  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.interface() != nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+  EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size());
+
+  elf.InitGnuDebugdata();
+  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+}
+
+class MockElf : public Elf {
+ public:
+  MockElf(Memory* memory) : Elf(memory) {}
+  virtual ~MockElf() = default;
+
+  void set_valid(bool valid) { valid_ = valid; }
+  void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
+};
+
+TEST_F(ElfTest, rel_pc) {
+  MockElf elf(memory_);
+
+  ElfInterface* interface = new ElfInterface32(memory_);
+  elf.set_elf_interface(interface);
+
+  elf.set_valid(true);
+  interface->set_load_bias(0);
+  MapInfo map_info{.start = 0x1000, .end = 0x2000};
+
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+
+  interface->set_load_bias(0x3000);
+  ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
+
+  elf.set_valid(false);
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
new file mode 100644
index 0000000..069386b
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
+  memset(ehdr, 0, sizeof(Ehdr));
+  memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+  ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+  ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+  ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+  ehdr->e_ident[EI_CLASS] = elf_class;
+  ehdr->e_type = ET_DYN;
+  ehdr->e_machine = machine_type;
+  ehdr->e_version = EV_CURRENT;
+  ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+static std::string GetTestFileDirectory() {
+  std::string exec(testing::internal::GetArgvs()[0]);
+  auto const value = exec.find_last_of('/');
+  if (value == std::string::npos) {
+    return "tests/files/";
+  }
+  return exec.substr(0, value + 1) + "tests/files/";
+}
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata,
+                          TestCopyFuncType copy_func) {
+  Ehdr ehdr;
+
+  TestInitEhdr(&ehdr, elf_class, machine);
+
+  uint64_t offset = sizeof(Ehdr);
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  copy_func(0, &ehdr, sizeof(ehdr));
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NULL;
+  copy_func(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // Skip this header, it will contain the gnu_debugdata information.
+  uint64_t gnu_offset = offset;
+  offset += ehdr.e_shentsize;
+
+  uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_name = 1;
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_offset = symtab_offset;
+  shdr.sh_size = 0x100;
+  copy_func(offset, &shdr, sizeof(shdr));
+
+  char value = '\0';
+  uint64_t symname_offset = symtab_offset;
+  copy_func(symname_offset, &value, 1);
+  symname_offset++;
+  std::string name(".shstrtab");
+  copy_func(symname_offset, name.c_str(), name.size() + 1);
+  symname_offset += name.size() + 1;
+  name = ".gnu_debugdata";
+  copy_func(symname_offset, name.c_str(), name.size() + 1);
+
+  ssize_t bytes = 0x100;
+  offset = symtab_offset + 0x100;
+  if (init_gnu_debugdata) {
+    // Read in the compressed elf data and copy it in.
+    name = GetTestFileDirectory();
+    if (elf_class == ELFCLASS32) {
+      name += "elf32.xz";
+    } else {
+      name += "elf64.xz";
+    }
+    int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
+    ASSERT_NE(-1, fd) << "Cannot open " + name;
+    // Assumes the file is less than 1024 bytes.
+    std::vector<uint8_t> buf(1024);
+    bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
+    ASSERT_GT(bytes, 0);
+    // Make sure the file isn't too big.
+    ASSERT_NE(static_cast<size_t>(bytes), buf.size())
+        << "File " + name + " is too big, increase buffer size.";
+    close(fd);
+    buf.resize(bytes);
+    copy_func(offset, buf.data(), buf.size());
+  }
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = symname_offset - symtab_offset;
+  shdr.sh_addr = offset;
+  shdr.sh_offset = offset;
+  shdr.sh_size = bytes;
+  copy_func(gnu_offset, &shdr, sizeof(shdr));
+}
+
+template void TestInitEhdr<Elf32_Ehdr>(Elf32_Ehdr*, uint32_t, uint32_t);
+template void TestInitEhdr<Elf64_Ehdr>(Elf64_Ehdr*, uint32_t, uint32_t);
+
+template void TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(uint32_t, uint32_t, bool,
+                                                           TestCopyFuncType);
+template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool,
+                                                           TestCopyFuncType);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
new file mode 100644
index 0000000..6ef00e1
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -0,0 +1,35 @@
+/*
+ * 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_TESTS_ELF_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+
+#include <functional>
+
+namespace unwindstack {
+
+typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType;
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type);
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
+                          TestCopyFuncType copy_func);
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GenGnuDebugdata.cpp b/libunwindstack/tests/GenGnuDebugdata.cpp
new file mode 100644
index 0000000..2644582
--- /dev/null
+++ b/libunwindstack/tests/GenGnuDebugdata.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+template <typename Ehdr>
+void InitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine) {
+  memset(ehdr, 0, sizeof(Ehdr));
+  memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+  ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+  ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+  ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+  ehdr->e_ident[EI_CLASS] = elf_class;
+  ehdr->e_type = ET_DYN;
+  ehdr->e_machine = machine;
+  ehdr->e_version = EV_CURRENT;
+  ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+template <typename Ehdr, typename Shdr>
+void GenElf(Ehdr* ehdr, int fd) {
+  uint64_t offset = sizeof(Ehdr);
+  ehdr->e_shoff = offset;
+  ehdr->e_shnum = 3;
+  ehdr->e_shentsize = sizeof(Shdr);
+  ehdr->e_shstrndx = 2;
+  TEMP_FAILURE_RETRY(write(fd, ehdr, sizeof(Ehdr)));
+
+  Shdr shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_name = 0;
+  shdr.sh_type = SHT_NULL;
+  TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+  offset += ehdr->e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 11;
+  shdr.sh_addr = 0x5000;
+  shdr.sh_offset = 0x5000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x800;
+  TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+  offset += ehdr->e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x200;
+  shdr.sh_size = 24;
+  TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+
+  // Write out the name entries information.
+  lseek(fd, 0x200, SEEK_SET);
+  std::string name;
+  TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+  name = ".shstrtab";
+  TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+  name = ".debug_frame";
+  TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+}
+
+int main() {
+  int elf32_fd = TEMP_FAILURE_RETRY(open("elf32", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+  if (elf32_fd == -1) {
+    printf("Failed to create elf32: %s\n", strerror(errno));
+    return 1;
+  }
+
+  int elf64_fd = TEMP_FAILURE_RETRY(open("elf64", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+  if (elf64_fd == -1) {
+    printf("Failed to create elf64: %s\n", strerror(errno));
+    return 1;
+  }
+
+  Elf32_Ehdr ehdr32;
+  InitEhdr<Elf32_Ehdr>(&ehdr32, ELFCLASS32, EM_ARM);
+  GenElf<Elf32_Ehdr, Elf32_Shdr>(&ehdr32, elf32_fd);
+  close(elf32_fd);
+
+  Elf64_Ehdr ehdr64;
+  InitEhdr<Elf64_Ehdr>(&ehdr64, ELFCLASS64, EM_AARCH64);
+  GenElf<Elf64_Ehdr, Elf64_Shdr>(&ehdr64, elf64_fd);
+  close(elf64_fd);
+}
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
index 411594a..537ccaf 100644
--- a/libunwindstack/tests/LogFake.cpp
+++ b/libunwindstack/tests/LogFake.cpp
@@ -25,7 +25,6 @@
 #include "LogFake.h"
 
 // Forward declarations.
-class Backtrace;
 struct EventTagMap;
 struct AndroidLogEntry;
 
@@ -33,6 +32,8 @@
 
 std::string g_fake_log_print;
 
+namespace unwindstack {
+
 void ResetLogs() {
   g_fake_log_buf = "";
   g_fake_log_print = "";
@@ -46,6 +47,8 @@
   return g_fake_log_print;
 }
 
+}  // namespace unwindstack
+
 extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
   g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
   g_fake_log_buf += tag;
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
index 006d393..e1dc50d 100644
--- a/libunwindstack/tests/LogFake.h
+++ b/libunwindstack/tests/LogFake.h
@@ -19,8 +19,12 @@
 
 #include <string>
 
+namespace unwindstack {
+
 void ResetLogs();
 std::string GetFakeLogBuf();
 std::string GetFakeLogPrint();
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
new file mode 100644
index 0000000..d2aad49
--- /dev/null
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoCreateMemoryTest : public ::testing::Test {
+ protected:
+  template <typename Ehdr, typename Shdr>
+  static void InitElf(int fd, uint64_t file_offset, uint64_t sh_offset, uint8_t class_type) {
+    std::vector<uint8_t> buffer(20000);
+    memset(buffer.data(), 0, buffer.size());
+
+    Ehdr ehdr;
+    memset(&ehdr, 0, sizeof(ehdr));
+    memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+    ehdr.e_ident[EI_CLASS] = class_type;
+    ehdr.e_shoff = sh_offset;
+    ehdr.e_shentsize = sizeof(Shdr) + 100;
+    ehdr.e_shnum = 4;
+    memcpy(&buffer[file_offset], &ehdr, sizeof(ehdr));
+
+    ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
+  }
+
+  static void SetUpTestCase() {
+    std::vector<uint8_t> buffer(1024);
+    memset(buffer.data(), 0, buffer.size());
+    memcpy(buffer.data(), ELFMAG, SELFMAG);
+    buffer[EI_CLASS] = ELFCLASS32;
+    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+    memset(buffer.data(), 0, buffer.size());
+    memcpy(&buffer[0x100], ELFMAG, SELFMAG);
+    buffer[0x100 + EI_CLASS] = ELFCLASS64;
+    ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+
+    InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
+    InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
+  }
+
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+  }
+
+  MemoryFake* memory_;
+  std::shared_ptr<Memory> process_memory_;
+
+  static TemporaryFile elf_;
+
+  static TemporaryFile elf_at_100_;
+
+  static TemporaryFile elf32_at_map_;
+  static TemporaryFile elf64_at_map_;
+};
+TemporaryFile MapInfoCreateMemoryTest::elf_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
+TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
+
+TEST_F(MapInfoCreateMemoryTest, end_le_start) {
+  MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() == nullptr);
+
+  info.end = 0xff;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() == nullptr);
+
+  // Make sure this test is valid.
+  info.end = 0x101;
+  memory.reset(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
+  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+
+  // Read the entire file.
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+  ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
+  for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
+  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Read the valid part of the file.
+  std::vector<uint8_t> buffer(0x100);
+  ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+  ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
+  for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+  MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  uint8_t e_ident[SELFMAG + 1];
+  ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+  ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+  MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  uint8_t e_ident[SELFMAG + 1];
+  ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+  ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
+  // Set up some memory so that a valid local memory object would
+  // be returned if the file mapping fails, but the device check is incorrect.
+  std::vector<uint8_t> buffer(1024);
+  MapInfo info;
+  info.start = reinterpret_cast<uint64_t>(buffer.data());
+  info.end = info.start + buffer.size();
+  info.offset = 0;
+
+  info.flags = 0x8000;
+  info.name = "/dev/something";
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoCreateMemoryTest, process_memory) {
+  MapInfo info;
+  info.start = 0x2000;
+  info.end = 0x3000;
+  info.offset = 0;
+
+  // Verify that the the process_memory object is used, so seed it
+  // with memory.
+  std::vector<uint8_t> buffer(1024);
+  for (size_t i = 0; i < buffer.size(); i++) {
+    buffer[i] = i % 256;
+  }
+  memory_->SetMemory(info.start, buffer.data(), buffer.size());
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(memory->Read(0, buffer.data(), buffer.size()));
+  for (size_t i = 0; i < buffer.size(); i++) {
+    ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
+  }
+
+  // Try to read outside of the map size.
+  ASSERT_FALSE(memory->Read(buffer.size(), buffer.data(), 1));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
new file mode 100644
index 0000000..0b70d13
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetElfTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+  }
+
+  template <typename Ehdr, typename Shdr>
+  static void InitElf(uint64_t sh_offset, Ehdr* ehdr, uint8_t class_type, uint8_t machine_type) {
+    memset(ehdr, 0, sizeof(*ehdr));
+    memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+    ehdr->e_ident[EI_CLASS] = class_type;
+    ehdr->e_machine = machine_type;
+    ehdr->e_shoff = sh_offset;
+    ehdr->e_shentsize = sizeof(Shdr) + 100;
+    ehdr->e_shnum = 4;
+  }
+
+  const size_t kMapSize = 4096;
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+
+  TemporaryFile elf_;
+};
+
+TEST_F(MapInfoGetElfTest, invalid) {
+  MapInfo info{.start = 0x1000, .end = 0x2000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+  // The map is empty, but this should still create an invalid elf object.
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, valid32) {
+  MapInfo info{.start = 0x3000, .end = 0x4000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+  EXPECT_EQ(ELFCLASS32, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, valid64) {
+  MapInfo info{.start = 0x8000, .end = 0x9000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+  memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+  EXPECT_EQ(ELFCLASS64, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
+  MapInfo info{.start = 0x4000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x4000 + offset, ptr, size);
+                                               });
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+  EXPECT_EQ(ELFCLASS32, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
+  MapInfo info{.start = 0x6000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x6000 + offset, ptr, size);
+                                               });
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+  EXPECT_EQ(ELFCLASS64, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
+  MapInfo info{.start = 0x2000, .end = 0x3000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x2000 + offset, ptr, size);
+                                               });
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+  EXPECT_EQ(ELFCLASS32, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
+  MapInfo info{.start = 0x5000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
+
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x5000 + offset, ptr, size);
+                                               });
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
+  ASSERT_TRUE(elf.get() != nullptr);
+  ASSERT_TRUE(elf->valid());
+  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+  EXPECT_EQ(ELFCLASS64, elf->class_type());
+  EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, end_le_start) {
+  MapInfo info{.start = 0x1000, .end = 0x1000, .offset = 0, .flags = PROT_READ, .name = elf_.path};
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_FALSE(elf->valid());
+
+  info.elf = nullptr;
+  info.end = 0xfff;
+  elf.reset(info.GetElf(process_memory_, false));
+  ASSERT_FALSE(elf->valid());
+
+  // Make sure this test is valid.
+  info.elf = nullptr;
+  info.end = 0x2000;
+  elf.reset(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf->valid());
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
+  MapInfo info{
+      .start = 0x1000, .end = 0x2000, .offset = 0x100, .flags = PROT_READ, .name = elf_.path};
+
+  std::vector<uint8_t> buffer(0x1000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memcpy(buffer.data(), &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+
+  // Read the entire file.
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), buffer.size()));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(elf->memory()->Read(buffer.size(), buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
+  MapInfo info{
+      .start = 0x1000, .end = 0x2000, .offset = 0x2000, .flags = PROT_READ, .name = elf_.path};
+
+  std::vector<uint8_t> buffer(0x4000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Read the valid part of the file.
+  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < 0x1000; i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+  MapInfo info{
+      .start = 0x5000, .end = 0x6000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+
+  std::vector<uint8_t> buffer(0x4000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+  MapInfo info{
+      .start = 0x7000, .end = 0x8000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+
+  std::vector<uint8_t> buffer(0x4000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
+  MapInfo info{.start = 0x9000, .end = 0xa000, .offset = 0x1000, .flags = 0, .name = ""};
+
+  // Create valid elf data in process memory only.
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_FALSE(elf->valid());
+
+  info.elf = nullptr;
+  info.flags = PROT_READ;
+  elf.reset(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, check_device_maps) {
+  MapInfo info{.start = 0x7000,
+               .end = 0x8000,
+               .offset = 0x1000,
+               .flags = PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+               .name = "/dev/something"};
+
+  // Create valid elf data in process memory for this to verify that only
+  // the name is causing invalid elf data.
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+  std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+  ASSERT_FALSE(elf->valid());
+
+  // Set the name to nothing to verify that it still fails.
+  info.elf = nullptr;
+  info.name = "";
+  elf.reset(info.GetElf(process_memory_, false));
+  ASSERT_FALSE(elf->valid());
+
+  // Change the flags and verify the elf is valid now.
+  info.elf = nullptr;
+  info.flags = PROT_READ;
+  elf.reset(info.GetElf(process_memory_, false));
+  ASSERT_TRUE(elf->valid());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
deleted file mode 100644
index c846ad7..0000000
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <elf.h>
-#include <errno.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/test_utils.h>
-#include <gtest/gtest.h>
-
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Memory.h"
-
-class MapInfoTest : public ::testing::Test {
- protected:
-  static void SetUpTestCase() {
-    std::vector<uint8_t> buffer(1024);
-    memcpy(buffer.data(), ELFMAG, SELFMAG);
-    for (size_t i = SELFMAG; i < buffer.size(); i++) {
-      buffer[i] = i / 256 + 1;
-    }
-    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
-
-    for (size_t i = 0; i < 0x100; i++) {
-      buffer[i] = i / 256 + 1;
-    }
-    memcpy(&buffer[0x100], ELFMAG, SELFMAG);
-    for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
-      buffer[i] = i / 256 + 1;
-    }
-    ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
-  }
-
-  static TemporaryFile elf_;
-
-  static TemporaryFile elf_at_100_;
-};
-TemporaryFile MapInfoTest::elf_;
-TemporaryFile MapInfoTest::elf_at_100_;
-
-TEST_F(MapInfoTest, end_le_start) {
-  MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
-
-  std::unique_ptr<Memory> memory;
-  memory.reset(info.CreateMemory(getpid()));
-  ASSERT_TRUE(memory.get() == nullptr);
-
-  info.end = 0xff;
-  memory.reset(info.CreateMemory(getpid()));
-  ASSERT_TRUE(memory.get() == nullptr);
-
-  // Make sure this test is valid.
-  info.end = 0x101;
-  memory.reset(info.CreateMemory(getpid()));
-  ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr);
-}
-
-// Verify that if the offset is non-zero but there is no elf at the offset,
-// that the full file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
-  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
-
-  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
-  ASSERT_TRUE(memory.get() != nullptr);
-  ASSERT_EQ(0x100U, info.elf_offset);
-
-  // Read the entire file.
-  std::vector<uint8_t> buffer(1024);
-  ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
-  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
-  for (size_t i = SELFMAG; i < buffer.size(); i++) {
-    ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
-  }
-
-  ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
-}
-
-// Verify that if the offset is non-zero and there is an elf at that
-// offset, that only part of the file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
-  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
-
-  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
-  ASSERT_TRUE(memory.get() != nullptr);
-  ASSERT_EQ(0U, info.elf_offset);
-
-  // Read the valid part of the file.
-  std::vector<uint8_t> buffer(0x100);
-  ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
-  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
-  for (size_t i = SELFMAG; i < buffer.size(); i++) {
-    ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
-  }
-
-  ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
-}
-
-// Verify that device file names will never result in Memory object creation.
-TEST_F(MapInfoTest, create_memory_check_device_maps) {
-  // Set up some memory so that a valid local memory object would
-  // be returned if the file mapping fails, but the device check is incorrect.
-  std::vector<uint8_t> buffer(1024);
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
-  std::unique_ptr<Memory> memory;
-
-  info.flags = 0x8000;
-  info.name = "/dev/something";
-  memory.reset(info.CreateMemory(getpid()));
-  ASSERT_TRUE(memory.get() == nullptr);
-}
-
-TEST_F(MapInfoTest, create_memory_local_memory) {
-  // Set up some memory for a valid local memory object.
-  std::vector<uint8_t> buffer(1024);
-  for (size_t i = 0; i < buffer.size(); i++) {
-    buffer[i] = i % 256;
-  }
-
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
-
-  std::unique_ptr<Memory> memory;
-  memory.reset(info.CreateMemory(getpid()));
-  ASSERT_TRUE(memory.get() != nullptr);
-
-  std::vector<uint8_t> read_buffer(1024);
-  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
-  for (size_t i = 0; i < read_buffer.size(); i++) {
-    ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
-  }
-
-  ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
-}
-
-TEST_F(MapInfoTest, create_memory_remote_memory) {
-  std::vector<uint8_t> buffer(1024);
-  memset(buffer.data(), 0xa, buffer.size());
-
-  pid_t pid;
-  if ((pid = fork()) == 0) {
-    while (true)
-      ;
-    exit(1);
-  }
-  ASSERT_LT(0, pid);
-
-  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
-  uint64_t iterations = 0;
-  siginfo_t si;
-  while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
-    usleep(30);
-    iterations++;
-    ASSERT_LT(iterations, 500000000ULL);
-  }
-
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
-
-  std::unique_ptr<Memory> memory;
-  memory.reset(info.CreateMemory(pid));
-  ASSERT_TRUE(memory.get() != nullptr);
-  // Set the local memory to a different value to guarantee we are reading
-  // from the remote process.
-  memset(buffer.data(), 0x1, buffer.size());
-  std::vector<uint8_t> read_buffer(1024);
-  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
-  for (size_t i = 0; i < read_buffer.size(); i++) {
-    ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
-  }
-
-  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
-
-  kill(pid, SIGKILL);
-}
-
-TEST_F(MapInfoTest, get_elf) {
-  // Create a map to use as initialization data.
-  void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  ASSERT_NE(MAP_FAILED, map);
-
-  uint64_t start = reinterpret_cast<uint64_t>(map);
-  MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
-
-  // The map contains garbage, but this should still produce an elf object.
-  Elf* elf = info.GetElf(getpid(), false);
-  ASSERT_TRUE(elf != nullptr);
-  ASSERT_FALSE(elf->valid());
-
-  ASSERT_EQ(0, munmap(map, 1024));
-}
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 7eb9bae..2d15a4e 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -14,21 +14,128 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <sys/mman.h>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Maps.h"
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
+
+static void VerifyLine(std::string line, MapInfo* info) {
+  BufferMaps maps(line.c_str());
+
+  if (info == nullptr) {
+    ASSERT_FALSE(maps.Parse()) << "Failed on: " + line;
+  } else {
+    ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
+    MapInfo* element = maps.Get(0);
+    ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
+    *info = *element;
+  }
+}
+
+TEST(MapsTest, verify_parse_line) {
+  MapInfo info;
+
+  VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
+  EXPECT_EQ(1U, info.start);
+  EXPECT_EQ(2U, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(3U, info.offset);
+  EXPECT_EQ("", info.name);
+
+  VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", &info);
+  EXPECT_EQ(0xaU, info.start);
+  EXPECT_EQ(0xbU, info.end);
+  EXPECT_EQ(0U, info.flags);
+  EXPECT_EQ(0xcU, info.offset);
+  EXPECT_EQ("/fake/name", info.name);
+
+  VerifyLine("01-02   rwxp   03    04:05    06    /fake/name/again\n", &info);
+  EXPECT_EQ(1U, info.start);
+  EXPECT_EQ(2U, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(3U, info.offset);
+  EXPECT_EQ("/fake/name/again", info.name);
+
+  VerifyLine("-00 rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00- rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 :00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00:00 \n", nullptr);
+  VerifyLine("x-00 rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00 -00 rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00x rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 0000:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00 :00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00: 00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00:000\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00:00 0/fake\n", nullptr);
+  VerifyLine("00-00 xxxx 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 ywxp 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 ryxp 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 rwyp 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 rwx- 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("0\n", nullptr);
+  VerifyLine("00\n", nullptr);
+  VerifyLine("00-\n", nullptr);
+  VerifyLine("00-0\n", nullptr);
+  VerifyLine("00-00\n", nullptr);
+  VerifyLine("00-00 \n", nullptr);
+  VerifyLine("00-00 -\n", nullptr);
+  VerifyLine("00-00 r\n", nullptr);
+  VerifyLine("00-00 --\n", nullptr);
+  VerifyLine("00-00 rw\n", nullptr);
+  VerifyLine("00-00 ---\n", nullptr);
+  VerifyLine("00-00 rwx\n", nullptr);
+  VerifyLine("00-00 ---s\n", nullptr);
+  VerifyLine("00-00 ---p\n", nullptr);
+  VerifyLine("00-00 ---s 0\n", nullptr);
+  VerifyLine("00-00 ---p 0 \n", nullptr);
+  VerifyLine("00-00 ---p 0 0\n", nullptr);
+  VerifyLine("00-00 ---p 0 0:\n", nullptr);
+  VerifyLine("00-00 ---p 0 0:0\n", nullptr);
+  VerifyLine("00-00 ---p 0 0:0 \n", nullptr);
+
+  // Line to verify that the parser will detect a completely malformed line
+  // properly.
+  VerifyLine("7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n",
+             nullptr);
+}
+
+TEST(MapsTest, verify_large_values) {
+  MapInfo info;
+#if defined(__LP64__)
+  VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
+  EXPECT_EQ(0xfabcdef012345678UL, info.start);
+  EXPECT_EQ(0xf12345678abcdef8UL, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(0xf0b0d0f010305070UL, info.offset);
+#else
+  VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", &info);
+  EXPECT_EQ(0xf2345678UL, info.start);
+  EXPECT_EQ(0xfabcdef8UL, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(0xf0305070UL, info.offset);
+#endif
+}
 
 TEST(MapsTest, parse_permissions) {
   BufferMaps maps(
-      "1000-2000 ---- 00000000 00:00 0\n"
-      "2000-3000 r--- 00000000 00:00 0\n"
-      "3000-4000 -w-- 00000000 00:00 0\n"
-      "4000-5000 --x- 00000000 00:00 0\n"
-      "5000-6000 rwx- 00000000 00:00 0\n");
+      "1000-2000 ---s 00000000 00:00 0\n"
+      "2000-3000 r--s 00000000 00:00 0\n"
+      "3000-4000 -w-s 00000000 00:00 0\n"
+      "4000-5000 --xp 00000000 00:00 0\n"
+      "5000-6000 rwxp 00000000 00:00 0\n");
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(5U, maps.Total());
@@ -68,28 +175,28 @@
 
 TEST(MapsTest, parse_name) {
   BufferMaps maps(
-      "720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
-      "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
-      "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+      "7b29b000-7b29e000 rw-p 00000000 00:00 0\n"
+      "7b29e000-7b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "7b29f000-7b2a0000 rw-p 00000000 00:00 0");
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
   auto it = maps.begin();
   ASSERT_EQ("", it->name);
-  ASSERT_EQ(0x720b29b000U, it->start);
-  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0x7b29b000U, it->start);
+  ASSERT_EQ(0x7b29e000U, it->end);
   ASSERT_EQ(0U, it->offset);
   ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
   ++it;
   ASSERT_EQ("/system/lib/fake.so", it->name);
-  ASSERT_EQ(0x720b29e000U, it->start);
-  ASSERT_EQ(0x720b29f000U, it->end);
+  ASSERT_EQ(0x7b29e000U, it->start);
+  ASSERT_EQ(0x7b29f000U, it->end);
   ASSERT_EQ(0U, it->offset);
   ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
   ++it;
   ASSERT_EQ("", it->name);
-  ASSERT_EQ(0x720b29f000U, it->start);
-  ASSERT_EQ(0x720b2a0000U, it->end);
+  ASSERT_EQ(0x7b29f000U, it->start);
+  ASSERT_EQ(0x7b2a0000U, it->end);
   ASSERT_EQ(0U, it->offset);
   ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
   ++it;
@@ -147,9 +254,9 @@
   ASSERT_TRUE(tf.fd != -1);
 
   ASSERT_TRUE(
-      android::base::WriteStringToFile("720b29b000-720b29e000 r-xp a0000000 00:00 0   /fake.so\n"
-                                       "720b2b0000-720b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
-                                       "720b2e0000-720b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
+      android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0   /fake.so\n"
+                                       "7b2b0000-7b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+                                       "7b2e0000-7b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
                                        tf.path, 0660, getuid(), getgid()));
 
   FileMaps maps(tf.path);
@@ -157,20 +264,20 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
   auto it = maps.begin();
-  ASSERT_EQ(0x720b29b000U, it->start);
-  ASSERT_EQ(0x720b29e000U, it->end);
+  ASSERT_EQ(0x7b29b000U, it->start);
+  ASSERT_EQ(0x7b29e000U, it->end);
   ASSERT_EQ(0xa0000000U, it->offset);
   ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
   ASSERT_EQ("/fake.so", it->name);
   ++it;
-  ASSERT_EQ(0x720b2b0000U, it->start);
-  ASSERT_EQ(0x720b2e0000U, it->end);
+  ASSERT_EQ(0x7b2b0000U, it->start);
+  ASSERT_EQ(0x7b2e0000U, it->end);
   ASSERT_EQ(0xb0000000U, it->offset);
   ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
   ASSERT_EQ("/fake2.so", it->name);
   ++it;
-  ASSERT_EQ(0x720b2e0000U, it->start);
-  ASSERT_EQ(0x720b2f0000U, it->end);
+  ASSERT_EQ(0x7b2e0000U, it->start);
+  ASSERT_EQ(0x7b2f0000U, it->end);
   ASSERT_EQ(0xc0000000U, it->offset);
   ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
   ASSERT_EQ("/fake3.so", it->name);
@@ -178,6 +285,163 @@
   ASSERT_EQ(it, maps.end());
 }
 
+TEST(MapsTest, file_no_map_name) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0\n"
+                                       "7b2b0000-7b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+                                       "7b2e0000-7b2f0000 r-xp c0000000 00:00 0 \n",
+                                       tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+  auto it = maps.begin();
+  ASSERT_EQ(0x7b29b000U, it->start);
+  ASSERT_EQ(0x7b29e000U, it->end);
+  ASSERT_EQ(0xa0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(0x7b2b0000U, it->start);
+  ASSERT_EQ(0x7b2e0000U, it->end);
+  ASSERT_EQ(0xb0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("/fake2.so", it->name);
+  ++it;
+  ASSERT_EQ(0x7b2e0000U, it->start);
+  ASSERT_EQ(0x7b2f0000U, it->end);
+  ASSERT_EQ(0xc0000000U, it->offset);
+  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
+  ASSERT_EQ("", it->name);
+  ++it;
+  ASSERT_EQ(it, maps.end());
+}
+
+// Verify that a file that crosses a buffer is parsed correctly.
+static std::string CreateEntry(size_t index) {
+  return android::base::StringPrintf("%08zx-%08zx rwxp 0000 00:00 0\n", index * 4096,
+                                     (index + 1) * 4096);
+}
+
+TEST(MapsTest, file_buffer_cross) {
+  constexpr size_t kBufferSize = 2048;
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  // Compute how many to add in the first buffer.
+  size_t entry_len = CreateEntry(0).size();
+  size_t index;
+  std::string file_data;
+  for (index = 0; index < kBufferSize / entry_len; index++) {
+    file_data += CreateEntry(index);
+  }
+  // Add a long name to make sure that the first buffer does not contain a
+  // complete line.
+  // Remove the last newline.
+  size_t extra = 0;
+  size_t leftover = kBufferSize % entry_len;
+  size_t overlap1_index = 0;
+  std::string overlap1_name;
+  if (leftover == 0) {
+    // Exact match, add a long name to cross over the value.
+    overlap1_name = "/fake/name/is/long/on/purpose";
+    file_data.erase(file_data.size() - 1);
+    file_data += ' ' + overlap1_name + '\n';
+    extra = entry_len + overlap1_name.size() + 1;
+    overlap1_index = index;
+  }
+
+  // Compute how many need to go in to hit the buffer boundary exactly.
+  size_t bytes_left_in_buffer = kBufferSize - extra;
+  size_t entries_to_add = bytes_left_in_buffer / entry_len + index;
+  for (; index < entries_to_add; index++) {
+    file_data += CreateEntry(index);
+  }
+
+  // Now figure out how many bytes to add to get exactly to the buffer boundary.
+  leftover = bytes_left_in_buffer % entry_len;
+  std::string overlap2_name;
+  size_t overlap2_index = 0;
+  if (leftover != 0) {
+    file_data.erase(file_data.size() - 1);
+    file_data += ' ';
+    overlap2_name = std::string(leftover - 1, 'x');
+    file_data += overlap2_name + '\n';
+    overlap2_index = index - 1;
+  }
+
+  // Now add a few entries on the next page.
+  for (size_t start = index; index < start + 10; index++) {
+    file_data += CreateEntry(index);
+  }
+
+  ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+  ASSERT_TRUE(maps.Parse());
+  EXPECT_EQ(index, maps.Total());
+  // Verify all of the maps.
+  for (size_t i = 0; i < index; i++) {
+    MapInfo* info = maps.Get(i);
+    ASSERT_TRUE(info != nullptr) << "Failed verifying index " + std::to_string(i);
+    EXPECT_EQ(i * 4096, info->start) << "Failed verifying index " + std::to_string(i);
+    EXPECT_EQ((i + 1) * 4096, info->end) << "Failed verifying index " + std::to_string(i);
+    EXPECT_EQ(0U, info->offset) << "Failed verifying index " + std::to_string(i);
+    if (overlap1_index != 0 && i == overlap1_index) {
+      EXPECT_EQ(overlap1_name, info->name) << "Failed verifying overlap1 name " + std::to_string(i);
+    } else if (overlap2_index != 0 && i == overlap2_index) {
+      EXPECT_EQ(overlap2_name, info->name) << "Failed verifying overlap2 name " + std::to_string(i);
+    } else {
+      EXPECT_EQ("", info->name) << "Failed verifying index " + std::to_string(i);
+    }
+  }
+}
+
+TEST(MapsTest, file_should_fail) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(android::base::WriteStringToFile(
+      "7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n", tf.path,
+      0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_FALSE(maps.Parse());
+}
+
+// Create a maps file that is extremely large.
+TEST(MapsTest, large_file) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  std::string file_data;
+  uint64_t start = 0x700000;
+  for (size_t i = 0; i < 5000; i++) {
+    file_data +=
+        android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r-xp 1000 00:0 0 /fake%zu.so\n",
+                                    start + i * 4096, start + (i + 1) * 4096, i);
+  }
+
+  ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5000U, maps.Total());
+  for (size_t i = 0; i < 5000; i++) {
+    MapInfo* info = maps.Get(i);
+    ASSERT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
+    ASSERT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
+    std::string name = "/fake" + std::to_string(i) + ".so";
+    ASSERT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
+  }
+}
+
 TEST(MapsTest, find) {
   BufferMaps maps(
       "1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
@@ -235,3 +499,5 @@
   ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
   ASSERT_EQ("/system/lib/fake5.so", info->name);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
similarity index 96%
rename from libunwindstack/tests/MemoryBuffer.cpp
rename to libunwindstack/tests/MemoryBufferTest.cpp
index af3d6b9..50a8a1b 100644
--- a/libunwindstack/tests/MemoryBuffer.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,10 +18,12 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "LogFake.h"
 
+namespace unwindstack {
+
 class MemoryBufferTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -75,3 +77,5 @@
 
   ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index afb1029..2026acc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -21,6 +21,8 @@
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
   const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
   for (size_t i = 0; i < length; i++, addr++) {
@@ -44,3 +46,5 @@
   }
   return true;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 70ef30a..d374261 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -23,7 +23,9 @@
 #include <vector>
 #include <unordered_map>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class MemoryFake : public Memory {
  public:
@@ -87,4 +89,6 @@
   }
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index aa7a23a..a204bae 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,9 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class MemoryFileTest : public ::testing::Test {
  protected:
@@ -269,3 +271,5 @@
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index ab999da..73eebdd 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -21,7 +21,9 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 TEST(MemoryLocalTest, read) {
   std::vector<uint8_t> src(1024);
@@ -64,3 +66,5 @@
   uint64_t value;
   ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index ee5ba01..680fae9 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -22,25 +22,20 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 
-class MemoryRangeTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    memory_ = new MemoryFake;
-  }
+namespace unwindstack {
 
-  MemoryFake* memory_;
-};
-
-TEST_F(MemoryRangeTest, read) {
+TEST(MemoryRangeTest, read) {
   std::vector<uint8_t> src(1024);
   memset(src.data(), 0x4c, 1024);
-  memory_->SetMemory(9001, src);
+  MemoryFake* memory_fake = new MemoryFake;
+  std::shared_ptr<Memory> process_memory(memory_fake);
+  memory_fake->SetMemory(9001, src);
 
-  MemoryRange range(memory_, 9001, 9001 + src.size());
+  MemoryRange range(process_memory, 9001, 9001 + src.size());
 
   std::vector<uint8_t> dst(1024);
   ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
@@ -49,12 +44,14 @@
   }
 }
 
-TEST_F(MemoryRangeTest, read_near_limit) {
+TEST(MemoryRangeTest, read_near_limit) {
   std::vector<uint8_t> src(4096);
   memset(src.data(), 0x4c, 4096);
-  memory_->SetMemory(1000, src);
+  MemoryFake* memory_fake = new MemoryFake;
+  std::shared_ptr<Memory> process_memory(memory_fake);
+  memory_fake->SetMemory(1000, src);
 
-  MemoryRange range(memory_, 1000, 2024);
+  MemoryRange range(process_memory, 1000, 2024);
 
   std::vector<uint8_t> dst(1024);
   ASSERT_TRUE(range.Read(1020, dst.data(), 4));
@@ -71,9 +68,12 @@
   ASSERT_TRUE(range.Read(1020, dst.data(), 4));
 }
 
-TEST_F(MemoryRangeTest, read_overflow) {
+TEST(MemoryRangeTest, read_overflow) {
   std::vector<uint8_t> buffer(100);
 
-  std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
+  std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
+  std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200));
   ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index e48edf7..a66d0c5 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -22,7 +22,6 @@
 #include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/types.h>
-#include <time.h>
 #include <unistd.h>
 
 #include <vector>
@@ -31,33 +30,21 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
 
 class MemoryRemoteTest : public ::testing::Test {
  protected:
-  static uint64_t NanoTime() {
-    struct timespec t = { 0, 0 };
-    clock_gettime(CLOCK_MONOTONIC, &t);
-    return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
-  }
-
   static bool Attach(pid_t pid) {
     if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
       return false;
     }
 
-    uint64_t start = NanoTime();
-    siginfo_t si;
-    while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
-      if ((NanoTime() - start) > 10 * NS_PER_SEC) {
-        printf("%d: Failed to stop after 10 seconds.\n", pid);
-        return false;
-      }
-      usleep(30);
-    }
-    return true;
+    return TestQuiescePid(pid);
   }
 
   static bool Detach(pid_t pid) {
@@ -77,6 +64,7 @@
     exit(1);
   }
   ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
 
   ASSERT_TRUE(Attach(pid));
 
@@ -89,8 +77,6 @@
   }
 
   ASSERT_TRUE(Detach(pid));
-
-  kill(pid, SIGKILL);
 }
 
 TEST_F(MemoryRemoteTest, read_fail) {
@@ -108,6 +94,7 @@
     exit(1);
   }
   ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
 
   ASSERT_TRUE(Attach(pid));
 
@@ -129,8 +116,6 @@
   ASSERT_EQ(0, munmap(src, pagesize));
 
   ASSERT_TRUE(Detach(pid));
-
-  kill(pid, SIGKILL);
 }
 
 TEST_F(MemoryRemoteTest, read_overflow) {
@@ -148,6 +133,7 @@
     exit(1);
   }
   ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
 
   ASSERT_TRUE(Attach(pid));
 
@@ -158,6 +144,6 @@
   ASSERT_FALSE(remote.Read(0, dst.data(), 100));
 
   ASSERT_TRUE(Detach(pid));
-
-  kill(pid, SIGKILL);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 51b5d7d..4a9ed9f 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -22,10 +22,12 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 TEST(MemoryTest, read32) {
   MemoryFakeAlwaysReadZero memory;
 
@@ -124,3 +126,5 @@
   ASSERT_TRUE(memory.ReadString(0, &dst_name));
   ASSERT_EQ("short", dst_name);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index ff030c8..efcd029 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -19,18 +19,67 @@
 
 #include <stdint.h>
 
-#include "Regs.h"
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
-template <typename TypeParam>
-class RegsFake : public RegsImpl<TypeParam> {
+namespace unwindstack {
+
+class RegsFake : public Regs {
  public:
   RegsFake(uint16_t total_regs, uint16_t sp_reg)
-      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+      : Regs(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
   virtual ~RegsFake() = default;
 
-  uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
-  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
-  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+  uint32_t MachineType() override { return fake_type_; }
+  void* RawData() override { return nullptr; }
+  uint64_t pc() override { return fake_pc_; }
+  uint64_t sp() override { return fake_sp_; }
+  bool SetPcFromReturnAddress(Memory*) override {
+    if (!fake_return_address_valid_) {
+      return false;
+    }
+    fake_pc_ = fake_return_address_;
+    return true;
+  }
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
+
+  bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+  void SetFromRaw() override {}
+
+  void FakeSetMachineType(uint32_t type) { fake_type_ = type; }
+  void FakeSetPc(uint64_t pc) { fake_pc_ = pc; }
+  void FakeSetSp(uint64_t sp) { fake_sp_ = sp; }
+  void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
+  void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
+
+ private:
+  uint32_t fake_type_ = 0;
+  uint64_t fake_pc_ = 0;
+  uint64_t fake_sp_ = 0;
+  bool fake_return_address_valid_ = false;
+  uint64_t fake_return_address_ = 0;
 };
 
+template <typename TypeParam>
+class RegsImplFake : public RegsImpl<TypeParam> {
+ public:
+  RegsImplFake(uint16_t total_regs, uint16_t sp_reg)
+      : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+  virtual ~RegsImplFake() = default;
+
+  uint32_t MachineType() override { return 0; }
+
+  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+  void SetFromRaw() override {}
+  bool SetPcFromReturnAddress(Memory*) override { return false; }
+  bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+  void FakeSetPc(uint64_t pc) { this->pc_ = pc; }
+  void FakeSetSp(uint64_t sp) { this->sp_ = sp; }
+};
+
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
new file mode 100644
index 0000000..85192d5
--- /dev/null
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+#include "Machine.h"
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class RegsStepIfSignalHandlerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    elf_memory_ = new MemoryFake;
+    elf_.reset(new Elf(elf_memory_));
+  }
+
+  void ArmStepIfSignalHandlerNonRt(uint32_t pc_data);
+  void ArmStepIfSignalHandlerRt(uint32_t pc_data);
+
+  MemoryFake* elf_memory_;
+  MemoryFake process_memory_;
+  std::unique_ptr<Elf> elf_;
+};
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerNonRt(uint32_t pc_data) {
+  uint64_t addr = 0x1000;
+  RegsArm regs;
+  regs[ARM_REG_PC] = 0x5000;
+  regs[ARM_REG_SP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData32(0x5000, pc_data);
+
+  for (uint64_t index = 0; index <= 30; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x100U, regs[ARM_REG_SP]);
+  EXPECT_EQ(0x120U, regs[ARM_REG_PC]);
+  EXPECT_EQ(0x100U, regs.sp());
+  EXPECT_EQ(0x120U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_non_rt) {
+  // Form 1
+  ArmStepIfSignalHandlerNonRt(0xe3a07077);
+
+  // Form 2
+  ArmStepIfSignalHandlerNonRt(0xef900077);
+
+  // Form 3
+  ArmStepIfSignalHandlerNonRt(0xdf002777);
+}
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerRt(uint32_t pc_data) {
+  uint64_t addr = 0x1000;
+  RegsArm regs;
+  regs[ARM_REG_PC] = 0x5000;
+  regs[ARM_REG_SP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData32(0x5000, pc_data);
+
+  for (uint64_t index = 0; index <= 100; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x350U, regs[ARM_REG_SP]);
+  EXPECT_EQ(0x370U, regs[ARM_REG_PC]);
+  EXPECT_EQ(0x350U, regs.sp());
+  EXPECT_EQ(0x370U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_rt) {
+  // Form 1
+  ArmStepIfSignalHandlerRt(0xe3a070ad);
+
+  // Form 2
+  ArmStepIfSignalHandlerRt(0xef9000ad);
+
+  // Form 3
+  ArmStepIfSignalHandlerRt(0xdf0027ad);
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm64_step_if_signal_handler) {
+  uint64_t addr = 0x1000;
+  RegsArm64 regs;
+  regs[ARM64_REG_PC] = 0x8000;
+  regs[ARM64_REG_SP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
+
+  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(0x460U, regs[ARM64_REG_SP]);
+  EXPECT_EQ(0x470U, regs[ARM64_REG_PC]);
+  EXPECT_EQ(0x460U, regs.sp());
+  EXPECT_EQ(0x470U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_no_siginfo) {
+  uint64_t addr = 0xa00;
+  RegsX86 regs;
+  regs[X86_REG_EIP] = 0x4100;
+  regs[X86_REG_ESP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
+  for (uint64_t index = 0; index <= 25; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x70U, regs[X86_REG_EBP]);
+  EXPECT_EQ(0x80U, regs[X86_REG_ESP]);
+  EXPECT_EQ(0x90U, regs[X86_REG_EBX]);
+  EXPECT_EQ(0xa0U, regs[X86_REG_EDX]);
+  EXPECT_EQ(0xb0U, regs[X86_REG_ECX]);
+  EXPECT_EQ(0xc0U, regs[X86_REG_EAX]);
+  EXPECT_EQ(0xf0U, regs[X86_REG_EIP]);
+  EXPECT_EQ(0x80U, regs.sp());
+  EXPECT_EQ(0xf0U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_siginfo) {
+  uint64_t addr = 0xa00;
+  RegsX86 regs;
+  regs[X86_REG_EIP] = 0x4100;
+  regs[X86_REG_ESP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
+  addr += 8;
+  // Pointer to ucontext data.
+  process_memory_.SetData32(addr, 0x8100);
+
+  addr = 0x8100;
+  for (uint64_t index = 0; index <= 30; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+  EXPECT_EQ(0xb0U, regs[X86_REG_EBP]);
+  EXPECT_EQ(0xc0U, regs[X86_REG_ESP]);
+  EXPECT_EQ(0xd0U, regs[X86_REG_EBX]);
+  EXPECT_EQ(0xe0U, regs[X86_REG_EDX]);
+  EXPECT_EQ(0xf0U, regs[X86_REG_ECX]);
+  EXPECT_EQ(0x100U, regs[X86_REG_EAX]);
+  EXPECT_EQ(0x130U, regs[X86_REG_EIP]);
+  EXPECT_EQ(0xc0U, regs.sp());
+  EXPECT_EQ(0x130U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_64_step_if_signal_handler) {
+  uint64_t addr = 0x500;
+  RegsX86_64 regs;
+  regs[X86_64_REG_RIP] = 0x7000;
+  regs[X86_64_REG_RSP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748);
+  elf_memory_->SetData16(0x7008, 0x0f05);
+
+  for (uint64_t index = 0; index <= 30; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x7000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x140U, regs[X86_64_REG_RSP]);
+  EXPECT_EQ(0x150U, regs[X86_64_REG_RIP]);
+  EXPECT_EQ(0x140U, regs.sp());
+  EXPECT_EQ(0x150U, regs.pc());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3056373..3b9f92b 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -18,46 +18,16 @@
 
 #include <gtest/gtest.h>
 
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Regs.h>
 
+#include "ElfFake.h"
 #include "MemoryFake.h"
+#include "RegsFake.h"
 
-class ElfFake : public Elf {
- public:
-  ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
-  virtual ~ElfFake() = default;
-
-  void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
-};
-
-class ElfInterfaceFake : public ElfInterface {
- public:
-  ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
-  virtual ~ElfInterfaceFake() = default;
-
-  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
-
-  bool Init() override { return false; }
-  void InitHeaders() override {}
-  bool GetSoname(std::string*) override { return false; }
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
-  bool Step(uint64_t, Regs*, Memory*) override { return false; }
-};
-
-template <typename TypeParam>
-class RegsTestImpl : public RegsImpl<TypeParam> {
- public:
-  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
-      : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
-      : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
-  virtual ~RegsTestImpl() = default;
-
-  uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
-};
+namespace unwindstack {
 
 class RegsTest : public ::testing::Test {
  protected:
@@ -65,22 +35,16 @@
     memory_ = new MemoryFake;
     elf_.reset(new ElfFake(memory_));
     elf_interface_ = new ElfInterfaceFake(elf_->memory());
-    elf_->set_elf_interface(elf_interface_);
+    elf_->FakeSetInterface(elf_interface_);
   }
 
-  template <typename AddressType>
-  void regs_rel_pc();
-
-  template <typename AddressType>
-  void regs_return_address_register();
-
   ElfInterfaceFake* elf_interface_;
   MemoryFake* memory_;
   std::unique_ptr<ElfFake> elf_;
 };
 
 TEST_F(RegsTest, regs32) {
-  RegsTestImpl<uint32_t> regs32(50, 10);
+  RegsImplFake<uint32_t> regs32(50, 10);
   ASSERT_EQ(50U, regs32.total_regs());
   ASSERT_EQ(10U, regs32.sp_reg());
 
@@ -103,7 +67,7 @@
 }
 
 TEST_F(RegsTest, regs64) {
-  RegsTestImpl<uint64_t> regs64(30, 12);
+  RegsImplFake<uint64_t> regs64(30, 12);
   ASSERT_EQ(30U, regs64.total_regs());
   ASSERT_EQ(12U, regs64.sp_reg());
 
@@ -125,64 +89,6 @@
   ASSERT_EQ(10U, regs64[8]);
 }
 
-template <typename AddressType>
-void RegsTest::regs_rel_pc() {
-  RegsTestImpl<AddressType> regs(30, 12);
-
-  elf_interface_->set_load_bias(0);
-  MapInfo map_info{.start = 0x1000, .end = 0x2000};
-  regs.set_pc(0x1101);
-  ASSERT_EQ(0x101U, regs.GetRelPc(elf_.get(), &map_info));
-  elf_interface_->set_load_bias(0x3000);
-  ASSERT_EQ(0x3101U, regs.GetRelPc(elf_.get(), &map_info));
-}
-
-TEST_F(RegsTest, regs32_rel_pc) {
-  regs_rel_pc<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_rel_pc) {
-  regs_rel_pc<uint64_t>();
-}
-
-template <typename AddressType>
-void RegsTest::regs_return_address_register() {
-  RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
-
-  regs[5] = 0x12345;
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345U, value);
-}
-
-TEST_F(RegsTest, regs32_return_address_register) {
-  regs_return_address_register<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_return_address_register) {
-  regs_return_address_register<uint64_t>();
-}
-
-TEST_F(RegsTest, regs32_return_address_sp_offset) {
-  RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
-
-  regs.set_sp(0x2002);
-  memory_->SetData32(0x2000, 0x12345678);
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345678U, value);
-}
-
-TEST_F(RegsTest, regs64_return_address_sp_offset) {
-  RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
-
-  regs.set_sp(0x2008);
-  memory_->SetData64(0x2000, 0x12345678aabbccddULL);
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345678aabbccddULL, value);
-}
-
 TEST_F(RegsTest, rel_pc) {
   RegsArm64 arm64;
   ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get()));
@@ -209,7 +115,7 @@
   RegsArm arm;
 
   // Check fence posts.
-  elf_interface_->set_load_bias(0);
+  elf_interface_->FakeSetLoadBias(0);
   ASSERT_EQ(3U,  arm.GetAdjustedPc(0x5, elf_.get()));
   ASSERT_EQ(4U,  arm.GetAdjustedPc(0x4, elf_.get()));
   ASSERT_EQ(3U,  arm.GetAdjustedPc(0x3, elf_.get()));
@@ -217,7 +123,7 @@
   ASSERT_EQ(1U,  arm.GetAdjustedPc(0x1, elf_.get()));
   ASSERT_EQ(0U,  arm.GetAdjustedPc(0x0, elf_.get()));
 
-  elf_interface_->set_load_bias(0x100);
+  elf_interface_->FakeSetLoadBias(0x100);
   ASSERT_EQ(0xffU,  arm.GetAdjustedPc(0xff, elf_.get()));
   ASSERT_EQ(0x103U,  arm.GetAdjustedPc(0x105, elf_.get()));
   ASSERT_EQ(0x104U,  arm.GetAdjustedPc(0x104, elf_.get()));
@@ -227,13 +133,13 @@
   ASSERT_EQ(0x100U,  arm.GetAdjustedPc(0x100, elf_.get()));
 
   // Check thumb instructions handling.
-  elf_interface_->set_load_bias(0);
+  elf_interface_->FakeSetLoadBias(0);
   memory_->SetData32(0x2000, 0);
   ASSERT_EQ(0x2003U,  arm.GetAdjustedPc(0x2005, elf_.get()));
   memory_->SetData32(0x2000, 0xe000f000);
   ASSERT_EQ(0x2001U,  arm.GetAdjustedPc(0x2005, elf_.get()));
 
-  elf_interface_->set_load_bias(0x400);
+  elf_interface_->FakeSetLoadBias(0x400);
   memory_->SetData32(0x2100, 0);
   ASSERT_EQ(0x2503U,  arm.GetAdjustedPc(0x2505, elf_.get()));
   memory_->SetData32(0x2100, 0xf111f111);
@@ -249,18 +155,60 @@
   MapInfo map_info{.start = 0x1000, .end = 0x2000};
 
   regs_arm.set_pc(0x1500);
-  ASSERT_EQ(0x500U, regs_arm.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x500U, invalid_elf.GetRelPc(regs_arm.pc(), &map_info));
   ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf));
 
   regs_arm64.set_pc(0x1600);
-  ASSERT_EQ(0x600U, regs_arm64.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x600U, invalid_elf.GetRelPc(regs_arm64.pc(), &map_info));
   ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf));
 
   regs_x86.set_pc(0x1700);
-  ASSERT_EQ(0x700U, regs_x86.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x700U, invalid_elf.GetRelPc(regs_x86.pc(), &map_info));
   ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf));
 
   regs_x86_64.set_pc(0x1800);
-  ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info));
+  ASSERT_EQ(0x800U, invalid_elf.GetRelPc(regs_x86_64.pc(), &map_info));
   ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
 }
+
+TEST_F(RegsTest, arm_set_from_raw) {
+  RegsArm arm;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
+  regs[13] = 0x100;
+  regs[15] = 0x200;
+  arm.SetFromRaw();
+  EXPECT_EQ(0x100U, arm.sp());
+  EXPECT_EQ(0x200U, arm.pc());
+}
+
+TEST_F(RegsTest, arm64_set_from_raw) {
+  RegsArm64 arm64;
+  uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
+  regs[31] = 0xb100000000ULL;
+  regs[32] = 0xc200000000ULL;
+  arm64.SetFromRaw();
+  EXPECT_EQ(0xb100000000U, arm64.sp());
+  EXPECT_EQ(0xc200000000U, arm64.pc());
+}
+
+TEST_F(RegsTest, x86_set_from_raw) {
+  RegsX86 x86;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
+  regs[4] = 0x23450000;
+  regs[8] = 0xabcd0000;
+  x86.SetFromRaw();
+  EXPECT_EQ(0x23450000U, x86.sp());
+  EXPECT_EQ(0xabcd0000U, x86.pc());
+}
+
+TEST_F(RegsTest, x86_64_set_from_raw) {
+  RegsX86_64 x86_64;
+  uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
+  regs[7] = 0x1200000000ULL;
+  regs[16] = 0x4900000000ULL;
+  x86_64.SetFromRaw();
+  EXPECT_EQ(0x1200000000U, x86_64.sp());
+  EXPECT_EQ(0x4900000000U, x86_64.pc());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index a0a21e6..da258a6 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -32,10 +32,13 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
 #include "MemoryFake.h"
 #include "Symbols.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class SymbolsTest : public ::testing::Test {
  protected:
@@ -333,3 +336,5 @@
 
 typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
new file mode 100644
index 0000000..8c31aa6
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.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 _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace unwindstack {
+
+class TestScopedPidReaper {
+ public:
+  TestScopedPidReaper(pid_t pid) : pid_(pid) {}
+  ~TestScopedPidReaper() {
+    kill(pid_, SIGKILL);
+    waitpid(pid_, nullptr, 0);
+  }
+
+ private:
+  pid_t pid_;
+};
+
+inline bool TestQuiescePid(pid_t pid) {
+  siginfo_t si;
+  bool ready = false;
+  // Wait for up to 5 seconds.
+  for (size_t i = 0; i < 5000; i++) {
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      ready = true;
+      break;
+    }
+    usleep(1000);
+  }
+  return ready;
+}
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
new file mode 100644
index 0000000..9f9ca8b
--- /dev/null
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,314 @@
+/*
+ * 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 <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static std::atomic_bool g_ready;
+static volatile bool g_ready_for_remote;
+static volatile bool g_signal_ready_for_remote;
+static std::atomic_bool g_finish;
+static std::atomic_uintptr_t g_ucontext;
+
+static void ResetGlobals() {
+  g_ready = false;
+  g_ready_for_remote = false;
+  g_signal_ready_for_remote = false;
+  g_finish = false;
+  g_ucontext = 0;
+}
+
+static std::vector<const char*> kFunctionOrder{"OuterFunction", "MiddleFunction", "InnerFunction"};
+
+static std::vector<const char*> kFunctionSignalOrder{"OuterFunction",        "MiddleFunction",
+                                                     "InnerFunction",        "SignalOuterFunction",
+                                                     "SignalMiddleFunction", "SignalInnerFunction"};
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+  g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
+  while (!g_finish.load()) {
+  }
+}
+
+extern "C" void SignalInnerFunction() {
+  g_signal_ready_for_remote = true;
+  while (!g_finish.load()) {
+  }
+}
+
+extern "C" void SignalMiddleFunction() {
+  SignalInnerFunction();
+}
+
+extern "C" void SignalOuterFunction() {
+  SignalMiddleFunction();
+}
+
+static void SignalCallerHandler(int, siginfo_t*, void*) {
+  SignalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder& unwinder) {
+  std::string unwind;
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    unwind += unwinder.FormatFrame(i) + '\n';
+  }
+
+  return std::string(
+             "Unwind completed without finding all frames\n"
+             "  Looking for function: ") +
+         function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
+                         std::vector<const char*> expected_function_names) {
+  auto process_memory(Memory::CreateProcessMemory(pid));
+
+  Unwinder unwinder(512, maps, regs, process_memory);
+  unwinder.Unwind();
+
+  std::string expected_function = expected_function_names.back();
+  expected_function_names.pop_back();
+  for (auto& frame : unwinder.frames()) {
+    if (frame.function_name == expected_function) {
+      if (expected_function_names.empty()) {
+        break;
+      }
+      expected_function = expected_function_names.back();
+      expected_function_names.pop_back();
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void InnerFunction(bool local, bool trigger_invalid_call) {
+  if (local) {
+    LocalMaps maps;
+    ASSERT_TRUE(maps.Parse());
+    std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+    RegsGetLocal(regs.get());
+
+    VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
+  } else {
+    g_ready_for_remote = true;
+    g_ready = true;
+    if (trigger_invalid_call) {
+      void (*crash_func)() = nullptr;
+      crash_func();
+    }
+    while (!g_finish.load()) {
+    }
+  }
+}
+
+extern "C" void MiddleFunction(bool local, bool trigger_invalid_call) {
+  InnerFunction(local, trigger_invalid_call);
+}
+
+extern "C" void OuterFunction(bool local, bool trigger_invalid_call) {
+  MiddleFunction(local, trigger_invalid_call);
+}
+
+class UnwindTest : public ::testing::Test {
+ public:
+  void SetUp() override { ResetGlobals(); }
+};
+
+TEST_F(UnwindTest, local) {
+  OuterFunction(true, false);
+}
+
+void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
+  *completed = false;
+  // Need to sleep before attempting first ptrace. Without this, on the
+  // host it becomes impossible to attach and ptrace sets errno to EPERM.
+  usleep(1000);
+  for (size_t i = 0; i < 1000; i++) {
+    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+      ASSERT_TRUE(TestQuiescePid(pid))
+          << "Waiting for process to quiesce failed: " << strerror(errno);
+
+      MemoryRemote memory(pid);
+      // Read the remote value to see if we are ready.
+      bool value;
+      if (memory.Read(addr, &value, sizeof(value)) && value) {
+        *completed = true;
+      }
+      if (!*completed || !leave_attached) {
+        ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+      }
+      if (*completed) {
+        break;
+      }
+    } else {
+      ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
+    }
+    usleep(5000);
+  }
+}
+
+TEST_F(UnwindTest, remote) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    OuterFunction(false, false);
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  bool completed;
+  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+  RemoteMaps maps(pid);
+  ASSERT_TRUE(maps.Parse());
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+      << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, from_context) {
+  std::atomic_int tid(0);
+  std::thread thread([&]() {
+    tid = syscall(__NR_gettid);
+    OuterFunction(false, false);
+  });
+
+  struct sigaction act, oldact;
+  memset(&act, 0, sizeof(act));
+  act.sa_sigaction = SignalHandler;
+  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+  ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+  // Wait for the tid to get set.
+  for (size_t i = 0; i < 100; i++) {
+    if (tid.load() != 0) {
+      break;
+    }
+    usleep(1000);
+  }
+  ASSERT_NE(0, tid.load());
+  // Portable tgkill method.
+  ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+
+  // Wait for context data.
+  void* ucontext;
+  for (size_t i = 0; i < 2000; i++) {
+    ucontext = reinterpret_cast<void*>(g_ucontext.load());
+    if (ucontext != nullptr) {
+      break;
+    }
+    usleep(1000);
+  }
+  ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
+
+  LocalMaps maps;
+  ASSERT_TRUE(maps.Parse());
+  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentMachineType(), ucontext));
+
+  VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
+
+  ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+  g_finish = true;
+  thread.join();
+}
+
+static void RemoteThroughSignal(int signal, unsigned int sa_flags) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    struct sigaction act, oldact;
+    memset(&act, 0, sizeof(act));
+    act.sa_sigaction = SignalCallerHandler;
+    act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
+    ASSERT_EQ(0, sigaction(signal, &act, &oldact));
+
+    OuterFunction(false, signal == SIGSEGV);
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  bool completed;
+  if (signal != SIGSEGV) {
+    WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
+    ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+    ASSERT_EQ(0, kill(pid, SIGUSR1));
+  }
+  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
+  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
+
+  RemoteMaps maps(pid);
+  ASSERT_TRUE(maps.Parse());
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+      << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, remote_through_signal) {
+  RemoteThroughSignal(SIGUSR1, 0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
+  RemoteThroughSignal(SIGUSR1, SA_SIGINFO);
+}
+
+TEST_F(UnwindTest, remote_through_signal_with_invalid_func) {
+  RemoteThroughSignal(SIGSEGV, 0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo_with_invalid_func) {
+  RemoteThroughSignal(SIGSEGV, SA_SIGINFO);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
new file mode 100644
index 0000000..16640a1
--- /dev/null
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -0,0 +1,613 @@
+/*
+ * 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 <elf.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+class MapsFake : public Maps {
+ public:
+  MapsFake() = default;
+  virtual ~MapsFake() = default;
+
+  bool Parse() { return true; }
+
+  void FakeClear() { maps_.clear(); }
+
+  void FakeAddMapInfo(const MapInfo& map_info) { maps_.push_back(map_info); }
+};
+
+class UnwinderTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    maps_.FakeClear();
+    MapInfo info;
+    info.name = "/system/fake/libc.so";
+    info.start = 0x1000;
+    info.end = 0x8000;
+    info.offset = 0;
+    info.flags = PROT_READ | PROT_WRITE;
+    ElfFake* elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    info.elf_offset = 0;
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "[stack]";
+    info.start = 0x10000;
+    info.end = 0x12000;
+    info.flags = PROT_READ | PROT_WRITE;
+    info.elf = nullptr;
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/dev/fake_device";
+    info.start = 0x13000;
+    info.end = 0x15000;
+    info.flags = PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP;
+    info.elf = nullptr;
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/system/fake/libunwind.so";
+    info.start = 0x20000;
+    info.end = 0x22000;
+    info.flags = PROT_READ | PROT_WRITE;
+    elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/fake/libanother.so";
+    info.start = 0x23000;
+    info.end = 0x24000;
+    info.flags = PROT_READ | PROT_WRITE;
+    elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info.name = "/fake/fake.apk";
+    info.start = 0x43000;
+    info.end = 0x44000;
+    info.offset = 0x1d000;
+    info.flags = PROT_READ | PROT_WRITE;
+    elf = new ElfFake(nullptr);
+    info.elf = elf;
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+  }
+
+  void SetUp() override {
+    ElfInterfaceFake::FakeClear();
+    regs_.FakeSetMachineType(EM_ARM);
+  }
+
+  static MapsFake maps_;
+  static RegsFake regs_;
+  static std::shared_ptr<Memory> process_memory_;
+};
+
+MapsFake UnwinderTest::maps_;
+RegsFake UnwinderTest::regs_(5, 0);
+std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
+
+TEST_F(UnwinderTest, multiple_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x1100U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_map_offset) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0x43000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x43000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.apk", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_offset);
+  EXPECT_EQ(0x43000U, frame->map_start);
+  EXPECT_EQ(0x44000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that no attempt to continue after the step indicates it is done.
+TEST_F(UnwinderTest, no_frames_after_finished) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x1000, 0x10000, true));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify the maximum frames to save.
+TEST_F(UnwinderTest, max_frames) {
+  for (size_t i = 0; i < 30; i++) {
+    ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame" + std::to_string(i), i));
+    ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
+  }
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x10000);
+
+  Unwinder unwinder(20, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(20U, unwinder.NumFrames());
+
+  for (size_t i = 0; i < 20; i++) {
+    auto* frame = &unwinder.frames()[i];
+    EXPECT_EQ(i, frame->num);
+    EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i;
+    EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i;
+    EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i;
+    EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
+    EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
+    EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
+    EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
+    EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
+    EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i;
+  }
+}
+
+// Verify that initial map names frames are removed.
+TEST_F(UnwinderTest, verify_frames_skipped) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x20000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10040, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1002, 0x10050, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10060, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  std::set<std::string> skip_set{"libunwind.so", "libanother.so"};
+  unwinder.Unwind(&skip_set);
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10050U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x1000U, frame->rel_pc);
+  EXPECT_EQ(0x21000U, frame->pc);
+  EXPECT_EQ(0x10060U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x20000U, frame->map_start);
+  EXPECT_EQ(0x22000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x23000U, frame->pc);
+  EXPECT_EQ(0x10070U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/fake/libanother.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x23000U, frame->map_start);
+  EXPECT_EQ(0x24000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify SP in a non-existant map is okay.
+TEST_F(UnwinderTest, sp_not_in_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x53000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x53000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x1000U, frame->rel_pc);
+  EXPECT_EQ(0x21000U, frame->pc);
+  EXPECT_EQ(0x50020U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x20000U, frame->map_start);
+  EXPECT_EQ(0x22000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify PC in a device stops the unwind.
+TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x13000);
+  regs_.FakeSetSp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify SP in a device stops the unwind.
+TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  regs_.FakeSetPc(0x1000);
+  regs_.FakeSetSp(0x13000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify a no map info frame gets a frame.
+TEST_F(UnwinderTest, pc_without_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.FakeSetPc(0x41000);
+  regs_.FakeSetSp(0x13000);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x41000U, frame->rel_pc);
+  EXPECT_EQ(0x41000U, frame->pc);
+  EXPECT_EQ(0x13000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added.
+TEST_F(UnwinderTest, speculative_frame) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  // Fake as if code called a nullptr function.
+  regs_.FakeSetPc(0);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetReturnAddress(0x1202);
+  regs_.FakeSetReturnAddressValid(true);
+
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x23100U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/libanother.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x23000U, frame->map_start);
+  EXPECT_EQ(0x24000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that a speculative frame is added then removed because no other
+// frames are added.
+TEST_F(UnwinderTest, speculative_frame_removed) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  // Fake as if code called a nullptr function.
+  regs_.FakeSetPc(0);
+  regs_.FakeSetSp(0x10000);
+  regs_.FakeSetReturnAddress(0x1202);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame_static) {
+  FrameData frame;
+  frame.num = 1;
+  frame.rel_pc = 0x1000;
+  frame.pc = 0x4000;
+  frame.sp = 0x1000;
+  frame.function_name = "function";
+  frame.function_offset = 100;
+  frame.map_name = "/fake/libfake.so";
+  frame.map_offset = 0x2000;
+  frame.map_start = 0x3000;
+  frame.map_end = 0x6000;
+  frame.map_flags = PROT_READ;
+
+  EXPECT_EQ("  #01 pc 0000000000001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, true));
+
+  frame.map_offset = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, true));
+
+  frame.function_offset = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
+
+  frame.function_name = "";
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", Unwinder::FormatFrame(frame, true));
+
+  frame.map_name = "";
+  EXPECT_EQ("  #01 pc 0000000000001000  <anonymous:3000>", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  <anonymous:3000>", Unwinder::FormatFrame(frame, true));
+
+  frame.map_start = 0;
+  frame.map_end = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  <unknown>", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  <unknown>", Unwinder::FormatFrame(frame, true));
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
+
+  regs_.FakeSetPc(0x2300);
+  regs_.FakeSetSp(0x10000);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  regs_.FakeSetMachineType(EM_ARM);
+  EXPECT_EQ("  #00 pc 00001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+  regs_.FakeSetMachineType(EM_386);
+  EXPECT_EQ("  #00 pc 00001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+
+  regs_.FakeSetMachineType(EM_AARCH64);
+  EXPECT_EQ("  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+  regs_.FakeSetMachineType(EM_X86_64);
+  EXPECT_EQ("  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+
+  EXPECT_EQ("", unwinder.FormatFrame(1));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/files/elf32.xz b/libunwindstack/tests/files/elf32.xz
new file mode 100644
index 0000000..f25d433
--- /dev/null
+++ b/libunwindstack/tests/files/elf32.xz
Binary files differ
diff --git a/libunwindstack/tests/files/elf64.xz b/libunwindstack/tests/files/elf64.xz
new file mode 100644
index 0000000..eb1618e
--- /dev/null
+++ b/libunwindstack/tests/files/elf64.xz
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
new file mode 100644
index 0000000..f2530d7
--- /dev/null
+++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#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 <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+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;
+}
+
+void DoUnwind(pid_t pid) {
+  unwindstack::RemoteMaps remote_maps(pid);
+  if (!remote_maps.Parse()) {
+    printf("Failed to parse map data.\n");
+    return;
+  }
+
+  unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+  if (regs == nullptr) {
+    printf("Unable to get remote reg data\n");
+    return;
+  }
+
+  printf("ABI: ");
+  switch (regs->MachineType()) {
+    case EM_ARM:
+      printf("arm");
+      break;
+    case EM_386:
+      printf("x86");
+      break;
+    case EM_AARCH64:
+      printf("arm64");
+      break;
+    case EM_X86_64:
+      printf("x86_64");
+      break;
+    default:
+      printf("unknown\n");
+      return;
+  }
+  printf("\n");
+
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+  unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+  unwinder.Unwind();
+
+  // Print the frames.
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    printf("%s\n", unwinder.FormatFrame(i).c_str());
+  }
+}
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Usage: unwind <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;
+  }
+
+  DoUnwind(pid);
+
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+
+  return 0;
+}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
new file mode 100644
index 0000000..66a9439
--- /dev/null
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "ArmExidx.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void DumpArm(ElfInterfaceArm* interface) {
+  if (interface == nullptr) {
+    printf("No ARM Unwind Information.\n\n");
+    return;
+  }
+
+  printf("ARM Unwind Information:\n");
+  for (const auto& entry : interface->pt_loads()) {
+    uint64_t load_bias = entry.second.table_offset;
+    printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
+           entry.second.table_size + load_bias);
+    for (auto addr : *interface) {
+      std::string name;
+      printf("  PC 0x%" PRIx64, addr + load_bias);
+      uint64_t func_offset;
+      uint64_t pc = addr + load_bias;
+      // This might be a thumb function, so set the low bit.
+      if (interface->GetFunctionName(pc | 1, &name, &func_offset) && !name.empty()) {
+        printf(" <%s>", name.c_str());
+      }
+      printf("\n");
+      uint64_t entry;
+      if (!interface->FindEntry(pc, &entry)) {
+        printf("    Cannot find entry for address.\n");
+        continue;
+      }
+      ArmExidx arm(nullptr, interface->memory(), nullptr);
+      arm.set_log(true);
+      arm.set_log_skip_execution(true);
+      arm.set_log_indent(2);
+      if (!arm.ExtractEntryData(entry)) {
+        if (arm.status() != ARM_STATUS_NO_UNWIND) {
+          printf("    Error trying to extract data.\n");
+        }
+        continue;
+      }
+      if (arm.data()->size() > 0) {
+        if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
+          printf("      Error trying to evaluate dwarf data.\n");
+        }
+      }
+    }
+  }
+  printf("\n");
+}
+
+void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+  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) {
+      continue;
+    }
+    printf("\n  PC 0x%" PRIx64, fde->pc_start + load_bias);
+    std::string name;
+    uint64_t func_offset;
+    if (interface->GetFunctionName(fde->pc_start + load_bias, &name, &func_offset) &&
+        !name.empty()) {
+      printf(" <%s>", name.c_str());
+    }
+    printf("\n");
+    if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+      printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
+    }
+  }
+}
+
+int GetElfInfo(const char* file) {
+  // Send all log messages to stdout.
+  log_to_stdout(true);
+
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, 0)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
+  if (!elf.Init() || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", file);
+    return 1;
+  }
+
+  ElfInterface* interface = elf.interface();
+  if (elf.machine_type() == EM_ARM) {
+    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+    printf("\n");
+  }
+
+  if (interface->eh_frame() != nullptr) {
+    printf("eh_frame information:\n");
+    DumpDwarfSection(interface, interface->eh_frame(), interface->load_bias());
+    printf("\n");
+  } else {
+    printf("\nno eh_frame information\n");
+  }
+
+  if (interface->debug_frame() != nullptr) {
+    printf("\ndebug_frame information:\n");
+    DumpDwarfSection(interface, interface->debug_frame(), interface->load_bias());
+    printf("\n");
+  } else {
+    printf("\nno debug_frame information\n");
+  }
+
+  // If there is a gnu_debugdata interface, dump the information for that.
+  ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+  if (gnu_debugdata_interface != nullptr) {
+    if (gnu_debugdata_interface->eh_frame() != nullptr) {
+      printf("\ngnu_debugdata (eh_frame):\n");
+      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+      printf("\n");
+    }
+    if (gnu_debugdata_interface->debug_frame() != nullptr) {
+      printf("\ngnu_debugdata (debug_frame):\n");
+      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+      printf("\n");
+    }
+  } else {
+    printf("\nno valid gnu_debugdata information\n");
+  }
+
+  return 0;
+}
+
+}  // namespace unwindstack
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Need to pass the name of an elf file to the program.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  return unwindstack::GetElfInfo(argv[1]);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
new file mode 100644
index 0000000..dc9ae5a
--- /dev/null
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+int main(int argc, char** argv) {
+  if (argc != 2 && argc != 3) {
+    printf("Usage: unwind_symbols <ELF_FILE> [<FUNC_ADDRESS>]\n");
+    printf("  Dump all function symbols in ELF_FILE. If FUNC_ADDRESS is\n");
+    printf("  specified, then get the function at that address.\n");
+    printf("  FUNC_ADDRESS must be a hex number.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  uint64_t func_addr;
+  if (argc == 3) {
+    char* name;
+    func_addr = strtoull(argv[2], &name, 16);
+    if (*name != '\0') {
+      printf("%s is not a hex number.\n", argv[2]);
+      return 1;
+    }
+  }
+
+  // Send all log messages to stdout.
+  unwindstack::log_to_stdout(true);
+
+  unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
+  if (!memory->Init(argv[1], 0)) {
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  unwindstack::Elf elf(memory);
+  if (!elf.Init() || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", argv[1]);
+    return 1;
+  }
+
+  switch (elf.machine_type()) {
+    case EM_ARM:
+      printf("ABI: arm\n");
+      break;
+    case EM_AARCH64:
+      printf("ABI: arm64\n");
+      break;
+    case EM_386:
+      printf("ABI: x86\n");
+      break;
+    case EM_X86_64:
+      printf("ABI: x86_64\n");
+      break;
+    default:
+      printf("ABI: unknown\n");
+      return 1;
+  }
+
+  std::string name;
+  uint64_t load_bias = elf.interface()->load_bias();
+  if (argc == 3) {
+    std::string cur_name;
+    uint64_t func_offset;
+    if (!elf.GetFunctionName(func_addr, &cur_name, &func_offset)) {
+      printf("No known function at 0x%" PRIx64 "\n", func_addr);
+      return 1;
+    }
+    printf("<0x%" PRIx64 ">", func_addr - func_offset);
+    if (func_offset != 0) {
+      printf("+%" PRId64, func_offset);
+    }
+    printf(": %s\n", cur_name.c_str());
+    return 0;
+  }
+
+  // This is a crude way to get the symbols in order.
+  for (const auto& entry : elf.interface()->pt_loads()) {
+    uint64_t start = entry.second.offset + load_bias;
+    uint64_t end = entry.second.table_size + load_bias;
+    for (uint64_t addr = start; addr < end; addr += 4) {
+      std::string cur_name;
+      uint64_t func_offset;
+      if (elf.GetFunctionName(addr, &cur_name, &func_offset)) {
+        if (cur_name != name) {
+          printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str());
+        }
+        name = cur_name;
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp
deleted file mode 100644
index 6f158b0..0000000
--- a/libunwindstack/unwind_info.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "ArmExidx.h"
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "ElfInterfaceArm.h"
-#include "Log.h"
-
-void DumpArm(ElfInterfaceArm* interface) {
-  if (interface == nullptr) {
-    printf("No ARM Unwind Information.\n\n");
-    return;
-  }
-
-  printf("ARM Unwind Information:\n");
-  for (const auto& entry : interface->pt_loads()) {
-    uint64_t load_bias = entry.second.table_offset;
-    printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
-           entry.second.table_size + load_bias);
-    for (auto addr : *interface) {
-      std::string name;
-      printf("  PC 0x%" PRIx64, addr + load_bias);
-      uint64_t func_offset;
-      if (interface->GetFunctionName(addr + load_bias + 1, &name, &func_offset) && !name.empty()) {
-        printf(" <%s>", name.c_str());
-      }
-      printf("\n");
-      uint64_t entry;
-      if (!interface->FindEntry(addr + load_bias, &entry)) {
-        printf("    Cannot find entry for address.\n");
-        continue;
-      }
-      ArmExidx arm(nullptr, interface->memory(), nullptr);
-      arm.set_log(true);
-      arm.set_log_skip_execution(true);
-      arm.set_log_indent(2);
-      if (!arm.ExtractEntryData(entry)) {
-        if (arm.status() != ARM_STATUS_NO_UNWIND) {
-          printf("    Error trying to extract data.\n");
-        }
-        continue;
-      }
-      if (arm.data()->size() > 0) {
-        if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
-          printf("      Error trying to evaluate dwarf data.\n");
-        }
-      }
-    }
-  }
-  printf("\n");
-}
-
-int main(int argc, char** argv) {
-  if (argc != 2) {
-    printf("Need to pass the name of an elf file to the program.\n");
-    return 1;
-  }
-
-  struct stat st;
-  if (stat(argv[1], &st) == -1) {
-    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
-    return 1;
-  }
-  if (!S_ISREG(st.st_mode)) {
-    printf("%s is not a regular file.\n", argv[1]);
-    return 1;
-  }
-  if (S_ISDIR(st.st_mode)) {
-    printf("%s is a directory.\n", argv[1]);
-    return 1;
-  }
-
-  // Send all log messages to stdout.
-  log_to_stdout(true);
-
-  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
-  if (!memory->Init(argv[1], 0)) {
-    // Initializatation failed.
-    printf("Failed to init\n");
-    return 1;
-  }
-
-  Elf elf(memory);
-  if (!elf.Init() || !elf.valid()) {
-    printf("%s is not a valid elf file.\n", argv[1]);
-    return 1;
-  }
-
-  ElfInterface* interface = elf.interface();
-  if (elf.machine_type() == EM_ARM) {
-    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
-    printf("\n");
-  }
-
-  return 0;
-}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index a0d6b9b..fc6f305 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -16,6 +16,10 @@
 
 cc_library {
     name: "libusbhost",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     host_supported: true,
     srcs: ["usbhost.c"],
     cflags: ["-Werror"],
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 508f553..b70845b 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,10 +18,12 @@
     host_supported: true,
 
     header_libs: [
+        "liblog_headers",
         "libsystem_headers",
         "libcutils_headers"
     ],
     export_header_lib_headers: [
+        "liblog_headers",
         "libsystem_headers",
         "libcutils_headers"
     ],
@@ -44,13 +46,16 @@
 cc_library {
     name: "libutils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     host_supported: true,
 
     srcs: [
         "CallStack.cpp",
         "FileMap.cpp",
         "JenkinsHash.cpp",
-        "Log.cpp",
         "NativeHandle.cpp",
         "Printer.cpp",
         "PropertyMap.cpp",
@@ -79,6 +84,10 @@
         "libutils_headers",
     ],
 
+    shared_libs: [
+        "liblog",
+    ],
+
     arch: {
         mips: {
             cflags: ["-DALIGN_DOUBLE"],
@@ -88,8 +97,6 @@
     target: {
         android: {
             srcs: [
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
                 "Trace.cpp",
             ],
 
@@ -99,7 +106,6 @@
                 "libbacktrace",
                 "libcutils",
                 "libdl",
-                "liblog",
                 "libvndksupport",
             ],
 
@@ -107,6 +113,12 @@
                 misc_undefined: ["integer"],
             },
         },
+        linux: {
+            srcs: [
+                "Looper.cpp",
+                "ProcessCallStack.cpp",
+            ],
+        },
 
         host: {
             cflags: ["-DLIBUTILS_NATIVE=1"],
@@ -116,18 +128,8 @@
             },
         },
 
-        linux: {
-            srcs: [
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
-            ],
-        },
         linux_bionic: {
             enabled: true,
-            srcs: [
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
-            ],
         },
 
         darwin: {
@@ -141,8 +143,6 @@
             enabled: true,
         },
     },
-
-    clang: true,
 }
 
 // Include subdirectory makefiles
diff --git a/libutils/Log.cpp b/libutils/Log.cpp
deleted file mode 100644
index 2c1fb86..0000000
--- a/libutils/Log.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- * 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 "Log"
-
-#include <utils/Log.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-LogIfSlow::LogIfSlow(
-        const char* tag, android_LogPriority priority, int timeoutMillis, const char* message)
-        : mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message),
-          mStart(systemTime(SYSTEM_TIME_BOOTTIME)) {
-}
-
-LogIfSlow::~LogIfSlow() {
-    int durationMillis = (int)nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart);
-    if (durationMillis > mTimeoutMillis) {
-        LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis);
-    }
-}
-
-} // namespace android
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 12f77bb..cbf042e 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -45,9 +45,11 @@
 #ifndef _WIN32
     if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
         ALOGE("%s: Failed to format string", __FUNCTION__);
+        va_end(arglist);
         return;
     }
 #else
+    va_end(arglist);
     return;
 #endif
 
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 24737b9..f5f881f 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -442,6 +442,11 @@
     // and all accesses to refs happen before its deletion in the final decWeak.
     // The destructor can safely access mRefs because either it's deleting
     // mRefs itself, or it's running entirely before the final mWeak decrement.
+    //
+    // Since we're doing atomic loads of `flags`, the static analyzer assumes
+    // they can change between `delete this;` and `refs->decWeak(id);`. This is
+    // not the case. The analyzer may become more okay with this patten when
+    // https://bugs.llvm.org/show_bug.cgi?id=34365 gets resolved. NOLINTNEXTLINE
     refs->decWeak(id);
 }
 
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 28fc351..73ec1be 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -23,9 +23,9 @@
 
 #include <utils/SystemClock.h>
 
-#include <sys/time.h>
 #include <string.h>
 #include <errno.h>
+#include <time.h>
 
 #include <cutils/compiler.h>
 
diff --git a/libutils/include/utils/Log.h b/libutils/include/utils/Log.h
index 5276a49..42e03e7 100644
--- a/libutils/include/utils/Log.h
+++ b/libutils/include/utils/Log.h
@@ -1,72 +1,7 @@
-/*
- * Copyright (C) 2005 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.
- */
+// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.
 
-//
-// C/C++ logging functions.  See the logging documentation for API details.
-//
-// We'd like these to be available from C code (in case we import some from
-// somewhere), so this has a C interface.
-//
-// The output will be correct when the log file is shared between multiple
-// threads and/or multiple processes so long as the operating system
-// supports O_APPEND.  These calls have mutex-protected data structures
-// and so are NOT reentrant.  Do not use LOG in a signal handler.
-//
-#ifndef _LIBS_UTILS_LOG_H
-#define _LIBS_UTILS_LOG_H
-
-#include <sys/types.h>
+// <log/log.h> has replaced this file and all changes should go there instead.
+// This path remains strictly to include that header as there are thousands of
+// references to <utils/Log.h> in the tree.
 
 #include <log/log.h>
-
-#ifdef __cplusplus
-
-namespace android {
-
-/*
- * A very simple utility that yells in the log when an operation takes too long.
- */
-class LogIfSlow {
-public:
-    LogIfSlow(const char* tag, android_LogPriority priority,
-            int timeoutMillis, const char* message);
-    ~LogIfSlow();
-
-private:
-    const char* const mTag;
-    const android_LogPriority mPriority;
-    const int mTimeoutMillis;
-    const char* const mMessage;
-    const int64_t mStart;
-};
-
-/*
- * Writes the specified debug log message if this block takes longer than the
- * specified number of milliseconds to run.  Includes the time actually taken.
- *
- * {
- *     ALOGD_IF_SLOW(50, "Excessive delay doing something.");
- *     doSomething();
- * }
- */
-#define ALOGD_IF_SLOW(timeoutMillis, message) \
-    android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
-
-} // namespace android
-
-#endif // __cplusplus
-
-#endif // _LIBS_UTILS_LOG_H
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index d106185..af6076c 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -28,6 +28,53 @@
 #include <utils/Errors.h>
 #include <utils/Timers.h>
 
+// Enable thread safety attributes only with clang.
+// The attributes can be safely erased when compiling with other compilers.
+#if defined(__clang__) && (!defined(SWIG))
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x)  // no-op
+#endif
+
+#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+    THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -44,24 +91,24 @@
  * The mutex must be unlocked by the thread that locked it.  They are not
  * recursive, i.e. the same thread can't lock it multiple times.
  */
-class Mutex {
-public:
+class CAPABILITY("mutex") Mutex {
+  public:
     enum {
         PRIVATE = 0,
         SHARED = 1
     };
 
-                Mutex();
-    explicit    Mutex(const char* name);
-    explicit    Mutex(int type, const char* name = NULL);
-                ~Mutex();
+    Mutex();
+    explicit Mutex(const char* name);
+    explicit Mutex(int type, const char* name = NULL);
+    ~Mutex();
 
     // lock or unlock the mutex
-    status_t    lock();
-    void        unlock();
+    status_t lock() ACQUIRE();
+    void unlock() RELEASE();
 
     // lock if possible; returns 0 on success, error otherwise
-    status_t    tryLock();
+    status_t tryLock() TRY_ACQUIRE(true);
 
 #if defined(__ANDROID__)
     // Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -75,32 +122,36 @@
     // which is subject to NTP adjustments, and includes time during suspend,
     // so a timeout may occur even though no processes could run.
     // Not holding a partial wakelock may lead to a system suspend.
-    status_t    timedLock(nsecs_t timeoutNs);
+    status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(true);
 #endif
 
     // Manages the mutex automatically. It'll be locked when Autolock is
     // constructed and released when Autolock goes out of scope.
-    class Autolock {
-    public:
-        inline explicit Autolock(Mutex& mutex) : mLock(mutex)  { mLock.lock(); }
-        inline explicit Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
-        inline ~Autolock() { mLock.unlock(); }
-    private:
+    class SCOPED_CAPABILITY Autolock {
+      public:
+        inline explicit Autolock(Mutex& mutex) ACQUIRE(mutex) : mLock(mutex) { mLock.lock(); }
+        inline explicit Autolock(Mutex* mutex) ACQUIRE(mutex) : mLock(*mutex) { mLock.lock(); }
+        inline ~Autolock() RELEASE() { mLock.unlock(); }
+
+      private:
         Mutex& mLock;
+        // Cannot be copied or moved - declarations only
+        Autolock(const Autolock&);
+        Autolock& operator=(const Autolock&);
     };
 
-private:
+  private:
     friend class Condition;
 
     // A mutex cannot be copied
-                Mutex(const Mutex&);
-    Mutex&      operator = (const Mutex&);
+    Mutex(const Mutex&);
+    Mutex& operator=(const Mutex&);
 
 #if !defined(_WIN32)
     pthread_mutex_t mMutex;
 #else
-    void    _init();
-    void*   mState;
+    void _init();
+    void* mState;
 #endif
 };
 
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index bdb2332..9afedd4 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -85,7 +85,7 @@
 #define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE)                 \
     template<> ::android::Mutex  \
         (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE);  \
-    template<> TYPE* ::android::Singleton< TYPE >::sInstance(0);  \
+    template<> TYPE* ::android::Singleton< TYPE >::sInstance(0);  /* NOLINT */ \
     template class ::android::Singleton< TYPE >;
 
 
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index f6433a8..cb3d338 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -67,7 +67,6 @@
 
     inline  const char16_t*     string() const;
 
-//TODO(b/35363681): remove
 private:
     static inline std::string   std_string(const String16& str);
 public:
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index f5f9219..1f3e5d8 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -67,7 +67,6 @@
     inline  const char*         c_str() const;
     inline  const char*         string() const;
 
-// TODO(b/35363681): remove
 private:
     static inline std::string   std_string(const String8& str);
 public:
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 0c20607..ae6d9c8 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -82,9 +82,10 @@
 
     // Accessors
 
-    inline  T&      operator* () const  { return *m_ptr; }
-    inline  T*      operator-> () const { return m_ptr;  }
-    inline  T*      get() const         { return m_ptr; }
+    inline T&       operator* () const     { return *m_ptr; }
+    inline T*       operator-> () const    { return m_ptr;  }
+    inline T*       get() const            { return m_ptr; }
+    inline explicit operator bool () const { return m_ptr != nullptr; }
 
     // Operators
 
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index 7b62c24..a891fca 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -23,6 +23,7 @@
     srcs: [
         "BitSet_test.cpp",
         "LruCache_test.cpp",
+        "Mutex_test.cpp",
         "Singleton_test.cpp",
         "String8_test.cpp",
         "StrongPointer_test.cpp",
@@ -33,8 +34,6 @@
     target: {
         android: {
             srcs: [
-                "Looper_test.cpp",
-                "RefBase_test.cpp",
                 "SystemClock_test.cpp",
             ],
             shared_libs: [
@@ -43,7 +42,6 @@
                 "libcutils",
                 "libutils",
                 "libbase",
-                "libdl",
             ],
         },
         linux: {
@@ -58,7 +56,6 @@
                 "liblog",
                 "libbase",
             ],
-            host_ldlibs: ["-ldl"],
         },
     },
 
@@ -71,6 +68,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wthread-safety",
     ],
 }
 
diff --git a/libunwindstack/Log.h b/libutils/tests/Mutex_test.cpp
similarity index 68%
copy from libunwindstack/Log.h
copy to libutils/tests/Mutex_test.cpp
index 2d01aa8..8a1805f 100644
--- a/libunwindstack/Log.h
+++ b/libutils/tests/Mutex_test.cpp
@@ -14,12 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#include <utils/Mutex.h>
 
-#include <stdint.h>
+#include <gtest/gtest.h>
 
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
+static android::Mutex mLock;
+static int i GUARDED_BY(mLock);
 
-#endif  // _LIBUNWINDSTACK_LOG_H
+void modifyLockedVariable() REQUIRES(mLock) {
+    i = 1;
+}
+
+TEST(Mutex, compile) {
+    android::Mutex::Autolock _l(mLock);
+    i = 0;
+    modifyLockedVariable();
+}
\ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 287a99c..e3faee3 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -50,36 +50,39 @@
         "libbase",
         "liblog",
     ],
-}
 
+    export_include_dirs: ["include"],
+}
 
 cc_library {
     name: "libziparchive",
     host_supported: true,
-    vendor_available:true,
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
-    shared_libs: ["liblog", "libbase"],
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
+    shared_libs: [
+        "liblog",
+        "libbase",
+        "libz",
+    ],
     target: {
         android: {
-            shared_libs: ["libz", "libutils"],
+            shared_libs: [
+                "libutils",
+            ],
         },
         host: {
             static_libs: ["libutils"],
         },
         linux_bionic: {
-            static_libs: ["libz"],
             enabled: true,
         },
-        linux: {
-            shared_libs: ["libz-host"],
-        },
-        darwin: {
-            shared_libs: ["libz-host"],
-        },
-        windows: {
-            shared_libs: ["libz-host"],
-        },
     },
 }
 
@@ -88,8 +91,11 @@
     name: "libziparchive-host",
     host_supported: true,
     device_supported: false,
-    defaults: ["libziparchive_defaults", "libziparchive_flags"],
-    shared_libs: ["libz-host"],
+    defaults: [
+        "libziparchive_defaults",
+        "libziparchive_flags",
+    ],
+    shared_libs: ["libz"],
     static_libs: ["libutils"],
 }
 
@@ -150,3 +156,13 @@
         },
     },
 }
+
+cc_binary {
+    name: "unzip",
+    defaults: ["libziparchive_flags"],
+    srcs: ["unzip.cpp"],
+    shared_libs: [
+        "libbase",
+        "libziparchive",
+    ],
+}
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index ddbc286..5fc2fb4 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -55,5 +55,4 @@
   return true;
 }
 
-
 #endif  // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
index 20715bb..d83d854 100644
--- a/libziparchive/entry_name_utils_test.cc
+++ b/libziparchive/entry_name_utils_test.cc
@@ -20,44 +20,43 @@
 
 TEST(entry_name_utils, NullChars) {
   // 'A', 'R', '\0', 'S', 'E'
-  const uint8_t zeroes[] = { 0x41, 0x52, 0x00, 0x53, 0x45 };
+  const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
   ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
 
-  const uint8_t zeroes_continuation_chars[] = { 0xc2, 0xa1, 0xc2, 0x00 };
-  ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars,
-                                sizeof(zeroes_continuation_chars)));
+  const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
+  ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
 }
 
 TEST(entry_name_utils, InvalidSequence) {
   // 0xfe is an invalid start byte
-  const uint8_t invalid[] = { 0x41, 0xfe };
+  const uint8_t invalid[] = {0x41, 0xfe};
   ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
 
   // 0x91 is an invalid start byte (it's a valid continuation byte).
-  const uint8_t invalid2[] = { 0x41, 0x91 };
+  const uint8_t invalid2[] = {0x41, 0x91};
   ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
 }
 
 TEST(entry_name_utils, TruncatedContinuation) {
   // Malayalam script with truncated bytes. There should be 2 bytes
   // after 0xe0
-  const uint8_t truncated[] = { 0xe0, 0xb4, 0x85, 0xe0, 0xb4 };
+  const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
   ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
 
   // 0xc2 is the start of a 2 byte sequence that we've subsequently
   // dropped.
-  const uint8_t truncated2[] = { 0xc2, 0xc2, 0xa1 };
+  const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
   ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
 }
 
 TEST(entry_name_utils, BadContinuation) {
   // 0x41 is an invalid continuation char, since it's MSBs
   // aren't "10..." (are 01).
-  const uint8_t bad[] = { 0xc2, 0xa1, 0xc2, 0x41 };
+  const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
   ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
 
   // 0x41 is an invalid continuation char, since it's MSBs
   // aren't "10..." (are 11).
-  const uint8_t bad2[] = { 0xc2, 0xa1, 0xc2, 0xfe };
+  const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
   ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
 }
diff --git a/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
similarity index 86%
rename from include/ziparchive/zip_archive.h
rename to libziparchive/include/ziparchive/zip_archive.h
index 31fc2df..73ae68d 100644
--- a/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -28,8 +28,8 @@
 
 /* Zip compression methods we support */
 enum {
-  kCompressStored     = 0,        // no compression
-  kCompressDeflated   = 8,        // standard deflate
+  kCompressStored = 0,    // no compression
+  kCompressDeflated = 8,  // standard deflate
 };
 
 struct ZipString {
@@ -44,19 +44,17 @@
   explicit ZipString(const char* entry_name);
 
   bool operator==(const ZipString& rhs) const {
-    return name && (name_length == rhs.name_length) &&
-        (memcmp(name, rhs.name, name_length) == 0);
+    return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
   }
 
   bool StartsWith(const ZipString& prefix) const {
     return name && (name_length >= prefix.name_length) &&
-        (memcmp(name, prefix.name, prefix.name_length) == 0);
+           (memcmp(name, prefix.name, prefix.name_length) == 0);
   }
 
   bool EndsWith(const ZipString& suffix) const {
     return name && (name_length >= suffix.name_length) &&
-        (memcmp(name + name_length - suffix.name_length, suffix.name,
-                suffix.name_length) == 0);
+           (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0);
   }
 };
 
@@ -71,8 +69,17 @@
   // Modification time. The zipfile format specifies
   // that the first two little endian bytes contain the time
   // and the last two little endian bytes contain the date.
+  // See `GetModificationTime`.
+  // TODO: should be overridden by extra time field, if present.
   uint32_t mod_time;
 
+  // Returns `mod_time` as a broken-down struct tm.
+  struct tm GetModificationTime() const;
+
+  // Suggested Unix mode for this entry, from the zip archive if created on
+  // Unix, or a default otherwise.
+  mode_t unix_mode;
+
   // 1 if this entry contains a data descriptor segment, 0
   // otherwise.
   uint8_t has_data_descriptor;
@@ -125,11 +132,11 @@
  *
  * Returns 0 on success, and negative values on failure.
  */
-int32_t OpenArchiveFd(const int fd, const char* debugFileName,
-                      ZipArchiveHandle *handle, bool assume_ownership = true);
+int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+                      bool assume_ownership = true);
 
 int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
-                              ZipArchiveHandle *handle);
+                              ZipArchiveHandle* handle);
 /*
  * Close archive, releasing resources associated with it. This will
  * unmap the central directory of the zipfile and free all internal
@@ -155,8 +162,7 @@
  * On non-Windows platforms this method does not modify internal state and
  * can be called concurrently.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
-                  ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
 
 /*
  * Start iterating over all entries of a zip file. The order of iteration
@@ -171,8 +177,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
                        const ZipString* optional_suffix);
 
 /*
@@ -208,8 +213,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
-                        uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
 
 int GetFileDescriptor(const ZipArchiveHandle handle);
 
@@ -221,9 +225,9 @@
 /*
  * Stream the uncompressed data through the supplied function,
  * passing cookie to it each time it gets called.
-*/
+ */
 int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
-        ProcessZipEntryFunction func, void* cookie);
+                                ProcessZipEntryFunction func, void* cookie);
 #endif
 
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
similarity index 96%
rename from include/ziparchive/zip_archive_stream_entry.h
rename to libziparchive/include/ziparchive/zip_archive_stream_entry.h
index a40b799..b4766f8 100644
--- a/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -40,7 +40,8 @@
 
   ZipArchiveHandle handle_;
 
-  uint32_t crc32_;
+  off64_t offset_ = 0;
+  uint32_t crc32_ = 0u;
 };
 
 #endif  // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
similarity index 96%
rename from include/ziparchive/zip_writer.h
rename to libziparchive/include/ziparchive/zip_writer.h
index 08ead48..c350a27 100644
--- a/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -19,7 +19,6 @@
 
 #include <cstdio>
 #include <ctime>
-#include <zlib.h>
 
 #include <memory>
 #include <string>
@@ -28,6 +27,9 @@
 #include "android-base/macros.h"
 #include "utils/Compat.h"
 
+struct z_stream_s;
+typedef struct z_stream_s z_stream;
+
 /**
  * Writes a Zip file via a stateful interface.
  *
@@ -50,7 +52,7 @@
  *   fclose(file);
  */
 class ZipWriter {
-public:
+ public:
   enum {
     /**
      * Flag to compress the zip entry using deflate.
@@ -120,8 +122,7 @@
   /**
    * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
    */
-  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
-                                    uint32_t alignment);
+  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment);
 
   /**
    * Writes bytes to the zip file for the previously started zip entry.
@@ -156,7 +157,7 @@
    */
   int32_t Finish();
 
-private:
+ private:
   DISALLOW_COPY_AND_ASSIGN(ZipWriter);
 
   int32_t HandleError(int32_t error_code);
@@ -179,7 +180,7 @@
   std::vector<FileEntry> files_;
   FileEntry current_file_entry_;
 
-  std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+  std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
 };
 
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
new file mode 100644
index 0000000..6756007
--- /dev/null
+++ b/libziparchive/unzip.cpp
@@ -0,0 +1,345 @@
+/*
+ * 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 <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <ziparchive/zip_archive.h>
+
+enum OverwriteMode {
+  kAlways,
+  kNever,
+  kPrompt,
+};
+
+static OverwriteMode overwrite_mode = kPrompt;
+static const char* flag_d = nullptr;
+static bool flag_l = false;
+static bool flag_p = false;
+static bool flag_q = false;
+static bool flag_v = false;
+static const char* archive_name = nullptr;
+static std::set<std::string> includes;
+static std::set<std::string> excludes;
+static uint64_t total_uncompressed_length = 0;
+static uint64_t total_compressed_length = 0;
+static size_t file_count = 0;
+
+static bool Filter(const std::string& name) {
+  if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
+  if (!includes.empty() && includes.find(name) == includes.end()) return true;
+  return false;
+}
+
+static bool MakeDirectoryHierarchy(const std::string& path) {
+  // stat rather than lstat because a symbolic link to a directory is fine too.
+  struct stat sb;
+  if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
+
+  // Ensure the parent directories exist first.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
+
+  // Then try to create this directory.
+  return (mkdir(path.c_str(), 0777) != -1);
+}
+
+static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
+  if (uncompressed == 0) return 0;
+  return (100LL * (uncompressed - compressed)) / uncompressed;
+}
+
+static void MaybeShowHeader() {
+  if (!flag_q) printf("Archive:  %s\n", archive_name);
+  if (flag_v) {
+    printf(
+        " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
+        "--------  ------  ------- ---- ---------- ----- --------  ----\n");
+  } else if (flag_l) {
+    printf(
+        "  Length      Date    Time    Name\n"
+        "---------  ---------- -----   ----\n");
+  }
+}
+
+static void MaybeShowFooter() {
+  if (flag_v) {
+    printf(
+        "--------          -------  ---                            -------\n"
+        "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
+        total_uncompressed_length, total_compressed_length,
+        CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+        (file_count == 1) ? "" : "s");
+  } else if (flag_l) {
+    printf(
+        "---------                     -------\n"
+        "%9" PRId64 "                     %zu file%s\n",
+        total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+  }
+}
+
+static bool PromptOverwrite(const std::string& dst) {
+  // TODO: [r]ename not implemented because it doesn't seem useful.
+  printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
+  fflush(stdout);
+  while (true) {
+    char* line = nullptr;
+    size_t n;
+    if (getline(&line, &n, stdin) == -1) {
+      error(1, 0, "(EOF/read error; assuming [N]one...)");
+      overwrite_mode = kNever;
+      return false;
+    }
+    if (n == 0) continue;
+    char cmd = line[0];
+    free(line);
+    switch (cmd) {
+      case 'y':
+        return true;
+      case 'n':
+        return false;
+      case 'A':
+        overwrite_mode = kAlways;
+        return true;
+      case 'N':
+        overwrite_mode = kNever;
+        return false;
+    }
+  }
+}
+
+static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // We need to extract to memory because ExtractEntryToFile insists on
+  // being able to seek and truncate, and you can't do that with stdout.
+  uint8_t* buffer = new uint8_t[entry.uncompressed_length];
+  int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+  if (err < 0) {
+    error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+  }
+  if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+    error(1, errno, "failed to write %s to stdout", name.c_str());
+  }
+  delete[] buffer;
+}
+
+static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  // Bad filename?
+  if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
+      name.find("/../") != std::string::npos) {
+    error(1, 0, "bad filename %s", name.c_str());
+  }
+
+  // Where are we actually extracting to (for human-readable output)?
+  std::string dst;
+  if (flag_d) {
+    dst = flag_d;
+    if (!android::base::EndsWith(dst, "/")) dst += '/';
+  }
+  dst += name;
+
+  // Ensure the directory hierarchy exists.
+  if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
+    error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+  }
+
+  // An entry in a zip file can just be a directory itself.
+  if (android::base::EndsWith(name, "/")) {
+    if (mkdir(name.c_str(), entry.unix_mode) == -1) {
+      // If the directory already exists, that's fine.
+      if (errno == EEXIST) {
+        struct stat sb;
+        if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
+      }
+      error(1, errno, "couldn't extract directory %s", dst.c_str());
+    }
+    return;
+  }
+
+  // Create the file.
+  int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
+  if (fd == -1 && errno == EEXIST) {
+    if (overwrite_mode == kNever) return;
+    if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
+    // Either overwrite_mode is kAlways or the user consented to this specific case.
+    fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
+  }
+  if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
+
+  // Actually extract into the file.
+  if (!flag_q) printf("  inflating: %s\n", dst.c_str());
+  int err = ExtractEntryToFile(zah, &entry, fd);
+  if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+  close(fd);
+}
+
+static void ListOne(const ZipEntry& entry, const std::string& name) {
+  tm t = entry.GetModificationTime();
+  char time[32];
+  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+           t.tm_mday, t.tm_hour, t.tm_min);
+  if (flag_v) {
+    printf("%8d  %s  %7d %3d%% %s %08x  %s\n", entry.uncompressed_length,
+           (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
+           CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
+           name.c_str());
+  } else {
+    printf("%9d  %s   %s\n", entry.uncompressed_length, time, name.c_str());
+  }
+}
+
+static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+  if (flag_l || flag_v) {
+    // -l or -lv or -lq or -v.
+    ListOne(entry, name);
+  } else {
+    // Actually extract.
+    if (flag_p) {
+      ExtractToPipe(zah, entry, name);
+    } else {
+      ExtractOne(zah, entry, name);
+    }
+  }
+  total_uncompressed_length += entry.uncompressed_length;
+  total_compressed_length += entry.compressed_length;
+  ++file_count;
+}
+
+static void ProcessAll(ZipArchiveHandle zah) {
+  MaybeShowHeader();
+
+  // libziparchive iteration order doesn't match the central directory.
+  // We could sort, but that would cost extra and wouldn't match either.
+  void* cookie;
+  int err = StartIteration(zah, &cookie, nullptr, nullptr);
+  if (err != 0) {
+    error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  ZipEntry entry;
+  ZipString string;
+  while ((err = Next(cookie, &entry, &string)) >= 0) {
+    std::string name(string.name, string.name + string.name_length);
+    if (!Filter(name)) ProcessOne(zah, entry, name);
+  }
+
+  if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+  EndIteration(cookie);
+
+  MaybeShowFooter();
+}
+
+static void ShowHelp(bool full) {
+  fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+  if (!full) exit(EXIT_FAILURE);
+
+  printf(
+      "\n"
+      "Extract FILEs from ZIP archive. Default is all files.\n"
+      "\n"
+      "-d DIR	Extract into DIR\n"
+      "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
+      "-n	Never overwrite files (default: prompt)\n"
+      "-o	Always overwrite files\n"
+      "-p	Pipe to stdout\n"
+      "-q	Quiet\n"
+      "-v	List contents verbosely\n"
+      "-x FILE	Exclude files\n");
+  exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+  static struct option opts[] = {
+      {"help", no_argument, 0, 'h'},
+  };
+  bool saw_x = false;
+  int opt;
+  while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+    switch (opt) {
+      case 'd':
+        flag_d = optarg;
+        break;
+      case 'h':
+        ShowHelp(true);
+        break;
+      case 'l':
+        flag_l = true;
+        break;
+      case 'n':
+        overwrite_mode = kNever;
+        break;
+      case 'o':
+        overwrite_mode = kAlways;
+        break;
+      case 'p':
+        flag_p = flag_q = true;
+        break;
+      case 'q':
+        flag_q = true;
+        break;
+      case 'v':
+        flag_v = true;
+        break;
+      case 'x':
+        saw_x = true;
+        break;
+      case 1:
+        // -x swallows all following arguments, so we use '-' in the getopt
+        // string and collect files here.
+        if (!archive_name) {
+          archive_name = optarg;
+        } else if (saw_x) {
+          excludes.insert(optarg);
+        } else {
+          includes.insert(optarg);
+        }
+        break;
+      default:
+        ShowHelp(false);
+    }
+  }
+
+  if (!archive_name) error(1, 0, "missing archive filename");
+
+  // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
+  ZipArchiveHandle zah;
+  int32_t err;
+  if ((err = OpenArchive(archive_name, &zah)) != 0) {
+    error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+  }
+
+  // Implement -d by changing into that directory.
+  // We'll create implicit directories based on paths in the zip file, but we
+  // require that the -d directory already exists.
+  if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+
+  ProcessAll(zah);
+
+  CloseArchive(zah);
+  return 0;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index d0bbd72..1be4061 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -27,6 +27,7 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <memory>
@@ -48,6 +49,10 @@
 
 using android::base::get_unaligned;
 
+// Used to turn on crc checks - verify that the content CRC matches the values
+// specified in the local file header and the central directory.
+static const bool kCrcChecksEnabled = false;
+
 // This is for windows. If we don't open a file in binary mode, weird
 // things will happen.
 #ifndef O_BINARY
@@ -57,68 +62,6 @@
 // The maximum number of bytes to scan backwards for the EOCD start.
 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
 
-static const char* kErrorMessages[] = {
-  "Unknown return code.",
-  "Iteration ended",
-  "Zlib error",
-  "Invalid file",
-  "Invalid handle",
-  "Duplicate entries in archive",
-  "Empty archive",
-  "Entry not found",
-  "Invalid offset",
-  "Inconsistent information",
-  "Invalid entry name",
-  "I/O Error",
-  "File mapping failed"
-};
-
-static const int32_t kErrorMessageUpperBound = 0;
-
-static const int32_t kIterationEnd = -1;
-
-// We encountered a Zlib error when inflating a stream from this file.
-// Usually indicates file corruption.
-static const int32_t kZlibError = -2;
-
-// The input file cannot be processed as a zip archive. Usually because
-// it's too small, too large or does not have a valid signature.
-static const int32_t kInvalidFile = -3;
-
-// An invalid iteration / ziparchive handle was passed in as an input
-// argument.
-static const int32_t kInvalidHandle = -4;
-
-// The zip archive contained two (or possibly more) entries with the same
-// name.
-static const int32_t kDuplicateEntry = -5;
-
-// The zip archive contains no entries.
-static const int32_t kEmptyArchive = -6;
-
-// The specified entry was not found in the archive.
-static const int32_t kEntryNotFound = -7;
-
-// The zip archive contained an invalid local file header pointer.
-static const int32_t kInvalidOffset = -8;
-
-// The zip archive contained inconsistent entry information. This could
-// be because the central directory & local file header did not agree, or
-// if the actual uncompressed length or crc32 do not match their declared
-// values.
-static const int32_t kInconsistentInformation = -9;
-
-// An invalid entry name was encountered.
-static const int32_t kInvalidEntryName = -10;
-
-// An I/O related system call (read, lseek, ftruncate, map) failed.
-static const int32_t kIoError = -11;
-
-// We were not able to mmap the central directory or entry contents.
-static const int32_t kMmapFailed = -12;
-
-static const int32_t kErrorMessageLowerBound = -13;
-
 /*
  * A Read-only Zip archive.
  *
@@ -172,8 +115,7 @@
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipString* hash_table,
-                            const uint32_t hash_table_size,
+static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
                             const ZipString& name) {
   const uint32_t hash = ComputeHash(name);
 
@@ -194,7 +136,7 @@
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
                          const ZipString& name) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
@@ -218,13 +160,12 @@
 }
 
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
-                                    off64_t file_length, off64_t read_amount,
-                                    uint8_t* scan_buffer) {
+                                    off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
 
-  if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
-    ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
-          static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
+  if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
+    ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
+          static_cast<int64_t>(search_start));
     return kIoError;
   }
 
@@ -255,8 +196,7 @@
    * Verify that there's no trailing space at the end of the central directory
    * and its comment.
    */
-  const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
-      + eocd->comment_length;
+  const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
   if (calculated_length != file_length) {
     ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
           static_cast<int64_t>(file_length - calculated_length));
@@ -269,7 +209,7 @@
    */
   if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
     ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
-        eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
+          eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
 #if defined(__ANDROID__)
     if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
       android_errorWriteLog(0x534e4554, "31251826");
@@ -282,8 +222,8 @@
     return kEmptyArchive;
   }
 
-  ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
-        eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
+  ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
+        eocd->cd_size, eocd->cd_start_offset);
 
   /*
    * It all looks good.  Create a mapping for the CD, and set the fields
@@ -312,7 +252,6 @@
  *   num_entries
  */
 static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
-
   // Test file length. We use lseek64 to make sure the file
   // is small enough to be a zip file (Its size must be less than
   // 0xffffffff bytes).
@@ -349,8 +288,8 @@
   }
 
   std::vector<uint8_t> scan_buffer(read_amount);
-  int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
-                                        scan_buffer.data());
+  int32_t result =
+      MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data());
   return result;
 }
 
@@ -371,8 +310,8 @@
    * least one unused entry to avoid an infinite loop during creation.
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
-  archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
-      sizeof(ZipString)));
+  archive->hash_table =
+      reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
   if (archive->hash_table == nullptr) {
     ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
           archive->hash_table_size, sizeof(ZipString));
@@ -394,8 +333,7 @@
       return -1;
     }
 
-    const CentralDirectoryRecord* cdr =
-        reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+    const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
     if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
       ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
       return -1;
@@ -404,7 +342,7 @@
     const off64_t local_header_offset = cdr->local_file_header_offset;
     if (local_header_offset >= archive->directory_offset) {
       ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
-          static_cast<int64_t>(local_header_offset), i);
+            static_cast<int64_t>(local_header_offset), i);
       return -1;
     }
 
@@ -414,8 +352,10 @@
     const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
 
     if (file_name + file_name_length > cd_end) {
-      ALOGW("Zip: file name boundary exceeds the central directory range, file_name_length: "
-            "%" PRIx16 ", cd_length: %zu", file_name_length, cd_length);
+      ALOGW(
+          "Zip: file name boundary exceeds the central directory range, file_name_length: "
+          "%" PRIx16 ", cd_length: %zu",
+          file_name_length, cd_length);
       return -1;
     }
     /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
@@ -427,8 +367,7 @@
     ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
-    const int add_result = AddToHash(archive->hash_table,
-        archive->hash_table_size, entry_name);
+    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
     if (add_result != 0) {
       ALOGW("Zip: Error adding entry to hash table %d", add_result);
       return add_result;
@@ -436,8 +375,7 @@
 
     ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
     if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
-      ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
-          ptr - cd_ptr, cd_length, i);
+      ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
       return -1;
     }
   }
@@ -462,8 +400,7 @@
   return 0;
 }
 
-static int32_t OpenArchiveInternal(ZipArchive* archive,
-                                   const char* debug_file_name) {
+static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
   int32_t result = -1;
   if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
     return result;
@@ -476,8 +413,8 @@
   return 0;
 }
 
-int32_t OpenArchiveFd(int fd, const char* debug_file_name,
-                      ZipArchiveHandle* handle, bool assume_ownership) {
+int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+                      bool assume_ownership) {
   ZipArchive* archive = new ZipArchive(fd, assume_ownership);
   *handle = archive;
   return OpenArchiveInternal(archive, debug_file_name);
@@ -497,7 +434,7 @@
 }
 
 int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
-                              ZipArchiveHandle *handle) {
+                              ZipArchiveHandle* handle) {
   ZipArchive* archive = new ZipArchive(address, length);
   *handle = archive;
   return OpenArchiveInternal(archive, debug_file_name);
@@ -512,26 +449,39 @@
   delete archive;
 }
 
-static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
-                                             ZipEntry *entry) {
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
   uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
-  if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
+  off64_t offset = entry->offset;
+  if (entry->method != kCompressStored) {
+    offset += entry->compressed_length;
+  } else {
+    offset += entry->uncompressed_length;
+  }
+
+  if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
     return kIoError;
   }
 
   const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
-  const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
-  const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
+  const uint16_t ddOffset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
+  const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + ddOffset);
 
-  entry->crc32 = descriptor->crc32;
-  entry->compressed_length = descriptor->compressed_size;
-  entry->uncompressed_length = descriptor->uncompressed_size;
+  // Validate that the values in the data descriptor match those in the central
+  // directory.
+  if (entry->compressed_length != descriptor->compressed_size ||
+      entry->uncompressed_length != descriptor->uncompressed_size ||
+      entry->crc32 != descriptor->crc32) {
+    ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+          "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+          entry->compressed_length, entry->uncompressed_length, entry->crc32,
+          descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
+    return kInconsistentInformation;
+  }
 
   return 0;
 }
 
-static int32_t FindEntry(const ZipArchive* archive, const int ent,
-                         ZipEntry* data) {
+static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) {
   const uint16_t nameLen = archive->hash_table[ent].name_length;
 
   // Recover the start of the central directory entry from the filename
@@ -549,8 +499,7 @@
     return kInvalidOffset;
   }
 
-  const CentralDirectoryRecord *cdr =
-      reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+  const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
 
   // The offset of the start of the central directory in the zipfile.
   // We keep this lying around so that we can sanity check all our lengths
@@ -578,46 +527,62 @@
   uint8_t lfh_buf[sizeof(LocalFileHeader)];
   if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
     ALOGW("Zip: failed reading lfh name from offset %" PRId64,
-        static_cast<int64_t>(local_header_offset));
+          static_cast<int64_t>(local_header_offset));
     return kIoError;
   }
 
-  const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
+  const LocalFileHeader* lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
 
   if (lfh->lfh_signature != LocalFileHeader::kSignature) {
     ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
-        static_cast<int64_t>(local_header_offset));
+          static_cast<int64_t>(local_header_offset));
     return kInvalidOffset;
   }
 
   // Paranoia: Match the values specified in the local file header
   // to those specified in the central directory.
 
-  // Verify that the central directory and local file header have the same general purpose bit
-  // flags set.
-  if (lfh->gpb_flags != cdr->gpb_flags) {
-    ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+  // Warn if central directory and local file header don't agree on the use
+  // of a trailing Data Descriptor. The reference implementation is inconsistent
+  // and appears to use the LFH value during extraction (unzip) but the CD value
+  // while displayng information about archives (zipinfo). The spec remains
+  // silent on this inconsistency as well.
+  //
+  // For now, always use the version from the LFH but make sure that the values
+  // specified in the central directory match those in the data descriptor.
+  //
+  // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
+  // bit 11 (EFS: The language encoding flag, marking that filename and comment are
+  // encoded using UTF-8). This implementation does not check for the presence of
+  // that flag and always enforces that entry names are valid UTF-8.
+  if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
+    ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
           cdr->gpb_flags, lfh->gpb_flags);
-    return kInconsistentInformation;
   }
 
   // If there is no trailing data descriptor, verify that the central directory and local file
   // header agree on the crc, compressed, and uncompressed sizes of the entry.
   if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
     data->has_data_descriptor = 0;
-    if (data->compressed_length != lfh->compressed_size
-        || data->uncompressed_length != lfh->uncompressed_size
-        || data->crc32 != lfh->crc32) {
-      ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
-        ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
-        data->compressed_length, data->uncompressed_length, data->crc32,
-        lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
+    if (data->compressed_length != lfh->compressed_size ||
+        data->uncompressed_length != lfh->uncompressed_size || data->crc32 != lfh->crc32) {
+      ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+            "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+            data->compressed_length, data->uncompressed_length, data->crc32, lfh->compressed_size,
+            lfh->uncompressed_size, lfh->crc32);
       return kInconsistentInformation;
     }
   } else {
     data->has_data_descriptor = 1;
   }
 
+  // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+  if ((cdr->version_made_by >> 8) == 3) {
+    data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+  } else {
+    data->unix_mode = 0777;
+  }
+
   // Check that the local file header name matches the declared
   // name in the central directory.
   if (lfh->file_name_length == nameLen) {
@@ -642,8 +607,8 @@
     return kInconsistentInformation;
   }
 
-  const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
-      + lfh->file_name_length + lfh->extra_field_length;
+  const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
+                              lfh->file_name_length + lfh->extra_field_length;
   if (data_offset > cd_offset) {
     ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
     return kInvalidOffset;
@@ -651,16 +616,17 @@
 
   if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
     ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
-      static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
+          static_cast<int64_t>(data_offset), data->compressed_length,
+          static_cast<int64_t>(cd_offset));
     return kInvalidOffset;
   }
 
   if (data->method == kCompressStored &&
-    static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
-     ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
-       static_cast<int64_t>(data_offset), data->uncompressed_length,
-       static_cast<int64_t>(cd_offset));
-     return kInvalidOffset;
+      static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
+    ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+          static_cast<int64_t>(data_offset), data->uncompressed_length,
+          static_cast<int64_t>(cd_offset));
+    return kInvalidOffset;
   }
 
   data->offset = data_offset;
@@ -675,8 +641,7 @@
   ZipString suffix;
   ZipArchive* archive;
 
-  IterationHandle(const ZipString* in_prefix,
-                  const ZipString* in_suffix) {
+  IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) {
     if (in_prefix) {
       uint8_t* name_copy = new uint8_t[in_prefix->name_length];
       memcpy(name_copy, in_prefix->name, in_prefix->name_length);
@@ -703,8 +668,7 @@
   }
 };
 
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipString* optional_prefix,
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
                        const ZipString* optional_suffix) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
 
@@ -717,7 +681,7 @@
   cookie->position = 0;
   cookie->archive = archive;
 
-  *cookie_ptr = cookie ;
+  *cookie_ptr = cookie;
   return 0;
 }
 
@@ -725,16 +689,14 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
-                  ZipEntry* data) {
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
   const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   if (entryName.name_length == 0) {
     ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
     return kInvalidEntryName;
   }
 
-  const int64_t ent = EntryToIndex(archive->hash_table,
-    archive->hash_table_size, entryName);
+  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
 
   if (ent < 0) {
     ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
@@ -762,10 +724,8 @@
 
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
     if (hash_table[i].name != NULL &&
-        (handle->prefix.name_length == 0 ||
-         hash_table[i].StartsWith(handle->prefix)) &&
-        (handle->suffix.name_length == 0 ||
-         hash_table[i].EndsWith(handle->suffix))) {
+        (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
@@ -785,8 +745,10 @@
  public:
   virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
   virtual ~Writer() {}
+
  protected:
   Writer() = default;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Writer);
 };
@@ -796,14 +758,12 @@
 // the data appended to it.
 class MemoryWriter : public Writer {
  public:
-  MemoryWriter(uint8_t* buf, size_t size) : Writer(),
-      buf_(buf), size_(size), bytes_written_(0) {
-  }
+  MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (bytes_written_ + buf_size > size_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
-            size_, bytes_written_ + buf_size);
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+            bytes_written_ + buf_size);
       return false;
     }
 
@@ -822,7 +782,6 @@
 // The file will be truncated to the end of the written data.
 class FileWriter : public Writer {
  public:
-
   // Creates a FileWriter for |fd| and prepare to write |entry| to it,
   // guaranteeing that the file descriptor is valid and that there's enough
   // space on the volume to write out the entry completely and that the file
@@ -881,8 +840,8 @@
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (total_bytes_written_ + buf_size > declared_length_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
-            declared_length_, total_bytes_written_ + buf_size);
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
+            total_bytes_written_ + buf_size);
       return false;
     }
 
@@ -895,13 +854,10 @@
 
     return result;
   }
+
  private:
-  FileWriter(const int fd, const size_t declared_length) :
-      Writer(),
-      fd_(fd),
-      declared_length_(declared_length),
-      total_bytes_written_(0) {
-  }
+  FileWriter(const int fd, const size_t declared_length)
+      : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
 
   const int fd_;
   const size_t declared_length_;
@@ -944,8 +900,7 @@
   zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
   if (zerr != Z_OK) {
     if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)",
-        ZLIB_VERSION);
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
     } else {
       ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
     }
@@ -954,19 +909,22 @@
   }
 
   auto zstream_deleter = [](z_stream* stream) {
-    inflateEnd(stream);  /* free up any allocated structures */
+    inflateEnd(stream); /* free up any allocated structures */
   };
 
   std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
 
   const uint32_t uncompressed_length = entry->uncompressed_length;
 
+  uint64_t crc = 0;
   uint32_t compressed_length = entry->compressed_length;
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
       const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
-      if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
+      off64_t offset = entry->offset + (entry->compressed_length - compressed_length);
+      // Make sure to read at offset to ensure concurrent access to the fd.
+      if (!mapped_zip.ReadAtOffset(read_buf.data(), getSize, offset)) {
         ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
         return kIoError;
       }
@@ -980,19 +938,19 @@
     /* uncompress the data */
     zerr = inflate(&zstream, Z_NO_FLUSH);
     if (zerr != Z_OK && zerr != Z_STREAM_END) {
-      ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
-          zerr, zstream.next_in, zstream.avail_in,
-          zstream.next_out, zstream.avail_out);
+      ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
+            zstream.avail_in, zstream.next_out, zstream.avail_out);
       return kZlibError;
     }
 
     /* write when we're full or when we're done */
-    if (zstream.avail_out == 0 ||
-      (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
+    if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
       const size_t write_size = zstream.next_out - &write_buf[0];
       if (!writer->Append(&write_buf[0], write_size)) {
         // The file might have declared a bogus length.
         return kInconsistentInformation;
+      } else {
+        crc = crc32(crc, &write_buf[0], write_size);
       }
 
       zstream.next_out = &write_buf[0];
@@ -1000,14 +958,19 @@
     }
   } while (zerr == Z_OK);
 
-  assert(zerr == Z_STREAM_END);     /* other errors should've been caught */
+  assert(zerr == Z_STREAM_END); /* other errors should've been caught */
 
-  // stream.adler holds the crc32 value for such streams.
-  *crc_out = zstream.adler;
+  // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
+  // "feature" of zlib to tell it there won't be a zlib file header. zlib
+  // doesn't bother calculating the checksum in that scenario. We just do
+  // it ourselves above because there are no additional gains to be made by
+  // having zlib calculate it for us, since they do it by calling crc32 in
+  // the same manner that we have above.
+  *crc_out = crc;
 
   if (zstream.total_out != uncompressed_length || compressed_length != 0) {
-    ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
-        zstream.total_out, uncompressed_length);
+    ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
+          uncompressed_length);
     return kInconsistentInformation;
   }
 
@@ -1015,7 +978,7 @@
 }
 
 static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
-                                 uint64_t *crc_out) {
+                                 uint64_t* crc_out) {
   static const uint32_t kBufSize = 32768;
   std::vector<uint8_t> buf(kBufSize);
 
@@ -1024,12 +987,15 @@
   uint64_t crc = 0;
   while (count < length) {
     uint32_t remaining = length - count;
+    off64_t offset = entry->offset + count;
 
-    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
-    // value.
+    // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
     const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
-    if (!mapped_zip.ReadData(buf.data(), block_size)) {
-      ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
+
+    // Make sure to read at offset to ensure concurrent access to the fd.
+    if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
+      ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s",
+            block_size, static_cast<int64_t>(offset), strerror(errno));
       return kIoError;
     }
 
@@ -1045,16 +1011,9 @@
   return 0;
 }
 
-int32_t ExtractToWriter(ZipArchiveHandle handle,
-                        ZipEntry* entry, Writer* writer) {
+int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   const uint16_t method = entry->method;
-  off64_t data_offset = entry->offset;
-
-  if (!archive->mapped_zip.SeekToOffset(data_offset)) {
-    ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
-    return kIoError;
-  }
 
   // this should default to kUnknownCompressionMethod.
   int32_t return_value = -1;
@@ -1066,15 +1025,14 @@
   }
 
   if (!return_value && entry->has_data_descriptor) {
-    return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
+    return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
     if (return_value) {
       return return_value;
     }
   }
 
-  // TODO: Fix this check by passing the right flags to inflate2 so that
-  // it calculates the CRC for us.
-  if (entry->crc32 != crc && false) {
+  // Validate that the CRC matches the calculated value.
+  if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
     ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
     return kInconsistentInformation;
   }
@@ -1082,14 +1040,12 @@
   return return_value;
 }
 
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
-                        uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
   std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
   return ExtractToWriter(handle, entry, writer.get());
 }
 
-int32_t ExtractEntryToFile(ZipArchiveHandle handle,
-                           ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
   std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
   if (writer.get() == nullptr) {
     return kIoError;
@@ -1099,19 +1055,24 @@
 }
 
 const char* ErrorCodeString(int32_t error_code) {
-  if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
-    return kErrorMessages[error_code * -1];
+  // Make sure that the number of entries in kErrorMessages and ErrorCodes
+  // match.
+  static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
+                "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
+
+  const uint32_t idx = -error_code;
+  if (idx < arraysize(kErrorMessages)) {
+    return kErrorMessages[idx];
   }
 
-  return kErrorMessages[0];
+  return "Unknown return code";
 }
 
 int GetFileDescriptor(const ZipArchiveHandle handle) {
   return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
 }
 
-ZipString::ZipString(const char* entry_name)
-    : name(reinterpret_cast<const uint8_t*>(entry_name)) {
+ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
   size_t len = strlen(entry_name);
   CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
   name_length = static_cast<uint16_t>(len);
@@ -1120,10 +1081,8 @@
 #if !defined(_WIN32)
 class ProcessWriter : public Writer {
  public:
-  ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
-    proc_function_(func),
-    cookie_(cookie) {
-  }
+  ProcessWriter(ProcessZipEntryFunction func, void* cookie)
+      : Writer(), proc_function_(func), cookie_(cookie) {}
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     return proc_function_(buf, buf_size, cookie_);
@@ -1140,7 +1099,7 @@
   return ExtractToWriter(handle, entry, &writer);
 }
 
-#endif //!defined(_WIN32)
+#endif  //! defined(_WIN32)
 
 int MappedZipFile::GetFileDescriptor() const {
   if (!has_fd_) {
@@ -1174,54 +1133,21 @@
   }
 }
 
-bool MappedZipFile::SeekToOffset(off64_t offset) {
-  if (has_fd_) {
-    if (lseek64(fd_, offset, SEEK_SET) != offset) {
-      ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
-      return false;
-    }
-    return true;
-  } else {
-    if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
-      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
-            data_length_);
-      return false;
-    }
-
-    read_pos_ = offset;
-    return true;
-  }
-}
-
-bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
-  if (has_fd_) {
-    if(!android::base::ReadFully(fd_, buffer, read_amount)) {
-      ALOGE("Zip: read from %d failed\n", fd_);
-      return false;
-    }
-  } else {
-    memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
-    read_pos_ += read_amount;
-  }
-  return true;
-}
-
 // Attempts to read |len| bytes into |buf| at offset |off|.
 bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
-#if !defined(_WIN32)
   if (has_fd_) {
-    if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
+    if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
       ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
       return false;
     }
-    return true;
+  } else {
+    if (off < 0 || off > static_cast<off64_t>(data_length_)) {
+      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
+      return false;
+    }
+    memcpy(buf, static_cast<uint8_t*>(base_ptr_) + off, len);
   }
-#endif
-  if (!SeekToOffset(off)) {
-    return false;
-  }
-  return ReadData(buf, len);
-
+  return true;
 }
 
 void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
@@ -1232,13 +1158,13 @@
 bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                             size_t cd_size) {
   if (mapped_zip.HasFd()) {
-    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
-                               cd_start_offset, cd_size, true /* read only */)) {
+    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
+                               cd_size, true /* read only */)) {
       return false;
     }
 
     CHECK_EQ(directory_map->getDataLength(), cd_size);
-    central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
+    central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
   } else {
     if (mapped_zip.GetBasePtr() == nullptr) {
       ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
@@ -1246,9 +1172,10 @@
     }
     if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
         mapped_zip.GetFileLength()) {
-      ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
-            "start_offset %"  PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
-            static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
+      ALOGE(
+          "Zip: Failed to map central directory, offset exceeds mapped memory region ("
+          "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
+          static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
       return false;
     }
 
@@ -1256,3 +1183,17 @@
   }
   return true;
 }
+
+tm ZipEntry::GetModificationTime() const {
+  tm t = {};
+
+  t.tm_hour = (mod_time >> 11) & 0x1f;
+  t.tm_min = (mod_time >> 5) & 0x3f;
+  t.tm_sec = (mod_time & 0x1f) << 1;
+
+  t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
+  t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
+  t.tm_mday = (mod_time >> 16) & 0x1f;
+
+  return t;
+}
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
index ca42509..8b99bde 100644
--- a/libziparchive/zip_archive_common.h
+++ b/libziparchive/zip_archive_common.h
@@ -57,6 +57,7 @@
   uint32_t cd_start_offset;
   // Length of the central directory comment.
   uint16_t comment_length;
+
  private:
   EocdRecord() = default;
   DISALLOW_COPY_AND_ASSIGN(EocdRecord);
@@ -73,7 +74,7 @@
 
   // The start of record signature. Must be |kSignature|.
   uint32_t record_signature;
-  // Tool version. Ignored by this implementation.
+  // Source tool version. Top byte gives source OS.
   uint16_t version_made_by;
   // Tool version. Ignored by this implementation.
   uint16_t version_needed;
@@ -106,11 +107,12 @@
   uint16_t file_start_disk;
   // File attributes. Ignored by this implementation.
   uint16_t internal_file_attributes;
-  // File attributes. Ignored by this implementation.
+  // File attributes. For archives created on Unix, the top bits are the mode.
   uint32_t external_file_attributes;
   // The offset to the local file header for this entry, from the
   // beginning of this archive.
   uint32_t local_file_header_offset;
+
  private:
   CentralDirectoryRecord() = default;
   DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
@@ -149,6 +151,7 @@
   // The length of the extra field info (in bytes). This data
   // will appear immediately after the entry file name.
   uint16_t extra_field_length;
+
  private:
   LocalFileHeader() = default;
   DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
@@ -164,6 +167,7 @@
   uint32_t compressed_size;
   // Uncompressed size of the entry.
   uint32_t uncompressed_size;
+
  private:
   DataDescriptor() = default;
   DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 971db4f..174aa3f 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -26,24 +26,79 @@
 
 #include <utils/FileMap.h>
 #include <ziparchive/zip_archive.h>
+#include "android-base/macros.h"
+
+static const char* kErrorMessages[] = {
+    "Success",
+    "Iteration ended",
+    "Zlib error",
+    "Invalid file",
+    "Invalid handle",
+    "Duplicate entries in archive",
+    "Empty archive",
+    "Entry not found",
+    "Invalid offset",
+    "Inconsistent information",
+    "Invalid entry name",
+    "I/O error",
+    "File mapping failed",
+};
+
+enum ErrorCodes : int32_t {
+  kIterationEnd = -1,
+
+  // We encountered a Zlib error when inflating a stream from this file.
+  // Usually indicates file corruption.
+  kZlibError = -2,
+
+  // The input file cannot be processed as a zip archive. Usually because
+  // it's too small, too large or does not have a valid signature.
+  kInvalidFile = -3,
+
+  // An invalid iteration / ziparchive handle was passed in as an input
+  // argument.
+  kInvalidHandle = -4,
+
+  // The zip archive contained two (or possibly more) entries with the same
+  // name.
+  kDuplicateEntry = -5,
+
+  // The zip archive contains no entries.
+  kEmptyArchive = -6,
+
+  // The specified entry was not found in the archive.
+  kEntryNotFound = -7,
+
+  // The zip archive contained an invalid local file header pointer.
+  kInvalidOffset = -8,
+
+  // The zip archive contained inconsistent entry information. This could
+  // be because the central directory & local file header did not agree, or
+  // if the actual uncompressed length or crc32 do not match their declared
+  // values.
+  kInconsistentInformation = -9,
+
+  // An invalid entry name was encountered.
+  kInvalidEntryName = -10,
+
+  // An I/O related system call (read, lseek, ftruncate, map) failed.
+  kIoError = -11,
+
+  // We were not able to mmap the central directory or entry contents.
+  kMmapFailed = -12,
+
+  kLastErrorCode = kMmapFailed,
+};
 
 class MappedZipFile {
  public:
-  explicit MappedZipFile(const int fd) :
-    has_fd_(true),
-    fd_(fd),
-    base_ptr_(nullptr),
-    data_length_(0),
-    read_pos_(0) {}
+  explicit MappedZipFile(const int fd)
+      : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
 
-  explicit MappedZipFile(void* address, size_t length) :
-    has_fd_(false),
-    fd_(-1),
-    base_ptr_(address),
-    data_length_(static_cast<off64_t>(length)),
-    read_pos_(0) {}
+  explicit MappedZipFile(void* address, size_t length)
+      : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
 
-  bool HasFd() const {return has_fd_;}
+  bool HasFd() const { return has_fd_; }
 
   int GetFileDescriptor() const;
 
@@ -51,10 +106,6 @@
 
   off64_t GetFileLength() const;
 
-  bool SeekToOffset(off64_t offset);
-
-  bool ReadData(uint8_t* buffer, size_t read_amount);
-
   bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);
 
  private:
@@ -68,19 +119,15 @@
 
   void* const base_ptr_;
   const off64_t data_length_;
-  // read_pos_ is the offset to the base_ptr_ where we read data from.
-  size_t read_pos_;
 };
 
 class CentralDirectory {
  public:
-  CentralDirectory(void) :
-    base_ptr_(nullptr),
-    length_(0) {}
+  CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
 
-  const uint8_t* GetBasePtr() const {return base_ptr_;}
+  const uint8_t* GetBasePtr() const { return base_ptr_; }
 
-  size_t GetMapLength() const {return length_;}
+  size_t GetMapLength() const { return length_; }
 
   void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
 
@@ -109,25 +156,25 @@
   uint32_t hash_table_size;
   ZipString* hash_table;
 
-  ZipArchive(const int fd, bool assume_ownership) :
-    mapped_zip(fd),
-    close_file(assume_ownership),
-    directory_offset(0),
-    central_directory(),
-    directory_map(new android::FileMap()),
-    num_entries(0),
-    hash_table_size(0),
-    hash_table(nullptr) {}
+  ZipArchive(const int fd, bool assume_ownership)
+      : mapped_zip(fd),
+        close_file(assume_ownership),
+        directory_offset(0),
+        central_directory(),
+        directory_map(new android::FileMap()),
+        num_entries(0),
+        hash_table_size(0),
+        hash_table(nullptr) {}
 
-  ZipArchive(void* address, size_t length) :
-    mapped_zip(address, length),
-    close_file(false),
-    directory_offset(0),
-    central_directory(),
-    directory_map(new android::FileMap()),
-    num_entries(0),
-    hash_table_size(0),
-    hash_table(nullptr) {}
+  ZipArchive(void* address, size_t length)
+      : mapped_zip(address, length),
+        close_file(false),
+        directory_offset(0),
+        central_directory(),
+        directory_map(new android::FileMap()),
+        num_entries(0),
+        hash_table_size(0),
+        hash_table(nullptr) {}
 
   ~ZipArchive() {
     if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
@@ -139,7 +186,6 @@
 
   bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                   size_t cd_size);
-
 };
 
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 3f336a6..9ec89b1 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -38,13 +38,8 @@
 static constexpr size_t kBufSize = 65535;
 
 bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
-  off64_t data_offset = entry.offset;
-  if (!archive->mapped_zip.SeekToOffset(data_offset)) {
-    ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
-    return false;
-  }
   crc32_ = entry.crc32;
+  offset_ = entry.offset;
   return true;
 }
 
@@ -61,11 +56,11 @@
  protected:
   bool Init(const ZipEntry& entry) override;
 
-  uint32_t length_;
+  uint32_t length_ = 0u;
 
  private:
   std::vector<uint8_t> data_;
-  uint32_t computed_crc32_;
+  uint32_t computed_crc32_ = 0u;
 };
 
 bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
@@ -89,7 +84,7 @@
   size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
   errno = 0;
-  if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
+  if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
     if (errno != 0) {
       ALOGE("Error reading from archive fd: %s", strerror(errno));
     } else {
@@ -104,6 +99,7 @@
   }
   computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
   length_ -= bytes;
+  offset_ += bytes;
   return &data_;
 }
 
@@ -129,9 +125,9 @@
   z_stream z_stream_;
   std::vector<uint8_t> in_;
   std::vector<uint8_t> out_;
-  uint32_t uncompressed_length_;
-  uint32_t compressed_length_;
-  uint32_t computed_crc32_;
+  uint32_t uncompressed_length_ = 0u;
+  uint32_t compressed_length_ = 0u;
+  uint32_t computed_crc32_ = 0u;
 };
 
 // This method is using libz macros with old-style-casts
@@ -162,8 +158,7 @@
   int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
   if (zerr != Z_OK) {
     if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)",
-        ZLIB_VERSION);
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
     } else {
       ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
     }
@@ -193,13 +188,14 @@
 
 bool ZipArchiveStreamEntryCompressed::Verify() {
   return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
-      crc32_ == computed_crc32_;
+         crc32_ == computed_crc32_;
 }
 
 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
   if (z_stream_.avail_out == 0) {
     z_stream_.next_out = out_.data();
-    z_stream_.avail_out = out_.size();;
+    z_stream_.avail_out = out_.size();
+    ;
   }
 
   while (true) {
@@ -210,7 +206,7 @@
       size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
       ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
       errno = 0;
-      if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
+      if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
         if (errno != 0) {
           ALOGE("Error reading from archive fd: %s", strerror(errno));
         } else {
@@ -220,15 +216,15 @@
       }
 
       compressed_length_ -= bytes;
+      offset_ += bytes;
       z_stream_.next_in = in_.data();
       z_stream_.avail_in = bytes;
     }
 
     int zerr = inflate(&z_stream_, Z_NO_FLUSH);
     if (zerr != Z_OK && zerr != Z_STREAM_END) {
-      ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
-          zerr, z_stream_.next_in, z_stream_.avail_in,
-          z_stream_.next_out, z_stream_.avail_out);
+      ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
+            z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
       return nullptr;
     }
 
@@ -276,8 +272,8 @@
   return length_ == 0;
 }
 
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
-    ZipArchiveHandle handle, const ZipEntry& entry) {
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
+                                                     const ZipEntry& entry) {
   ZipArchiveStreamEntry* stream = nullptr;
   if (entry.method != kCompressStored) {
     stream = new ZipArchiveStreamEntryCompressed(handle);
@@ -292,8 +288,8 @@
   return stream;
 }
 
-ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
-    ZipArchiveHandle handle, const ZipEntry& entry) {
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
+                                                        const ZipEntry& entry) {
   ZipArchiveStreamEntry* stream = nullptr;
   if (entry.method == kCompressStored) {
     // Not compressed, don't need to do anything special.
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 3035a5e..753bd44 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "zip_archive_private.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -42,21 +44,13 @@
 static const std::string kBadFilenameZip = "bad_filename.zip";
 static const std::string kUpdateZip = "dummy-update.zip";
 
-static const std::vector<uint8_t> kATxtContents {
-  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-  '\n'
-};
+static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
+                                                'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
 
-static const std::vector<uint8_t> kATxtContentsCompressed {
-  'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
-  132, 210, '\\', '\0'
-};
+static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I',  'M', 'K',
+                                                          207, 'H', 132, 210, '\\', '\0'};
 
-static const std::vector<uint8_t> kBTxtContents {
-  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-  '\n'
-};
+static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
 
 static const std::string kATxtName("a.txt");
 static const std::string kBTxtName("b.txt");
@@ -65,14 +59,12 @@
 static const std::string kLargeCompressTxtName("compress.txt");
 static const std::string kLargeUncompressTxtName("uncompress.txt");
 
-static int32_t OpenArchiveWrapper(const std::string& name,
-                                  ZipArchiveHandle* handle) {
+static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
   const std::string abs_path = test_data_dir + "/" + name;
   return OpenArchive(abs_path.c_str(), handle);
 }
 
-static void AssertNameEquals(const std::string& name_str,
-                             const ZipString& name) {
+static void AssertNameEquals(const std::string& name_str, const ZipString& name) {
   ASSERT_EQ(name_str.size(), name.name_length);
   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
 }
@@ -343,47 +335,36 @@
 }
 
 static const uint32_t kEmptyEntriesZip[] = {
-      0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
-      0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
-      0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
-      0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
-      0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
-      0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
-      0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
+    0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
+    0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
+    0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
+    0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
+    0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
+    0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
 
 // This is a zip file containing a single entry (ab.txt) that contains
 // 90072 repetitions of the string "ab\n" and has an uncompressed length
 // of 270216 bytes.
 static const uint16_t kAbZip[] = {
-  0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
-  0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
-  0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
-  0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
-  0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
-  0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
-  0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
-  0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
-  0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
-  0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
-  0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
-  0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
-  0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
-};
+    0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
+    0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+    0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
+    0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+    0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
+    0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+    0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
+    0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+    0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
 
 static const std::string kAbTxtName("ab.txt");
 static const size_t kAbUncompressedSize = 270216;
@@ -404,7 +385,6 @@
   uint8_t buffer[1];
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
 
-
   TemporaryFile tmp_output_file;
   ASSERT_NE(-1, tmp_output_file.fd);
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
@@ -418,7 +398,7 @@
   TemporaryFile tmp_file;
   ASSERT_NE(-1, tmp_file.fd);
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
-                         sizeof(kAbZip) - 1));
+                                        sizeof(kAbZip) - 1));
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
 
@@ -446,8 +426,7 @@
   // the same as the memory buffer we extracted directly to.
   std::vector<uint8_t> file_contents(kAbUncompressedSize);
   ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
-                                       file_contents.size()));
+  ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
   ASSERT_EQ(file_contents, buffer);
 
   for (int i = 0; i < 90072; ++i) {
@@ -463,7 +442,7 @@
   ASSERT_NE(-1, tmp_file.fd);
 
   // Create a file with 8 bytes of random garbage.
-  static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
+  static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
 
@@ -474,7 +453,7 @@
 TEST(ziparchive, ExtractToFile) {
   TemporaryFile tmp_file;
   ASSERT_NE(-1, tmp_file.fd);
-  const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
+  const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
   const size_t data_size = sizeof(data);
 
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
@@ -488,7 +467,6 @@
   ASSERT_EQ(0, FindEntry(handle, name, &entry));
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
 
-
   // Assert that the first 8 bytes of the file haven't been clobbered.
   uint8_t read_buffer[data_size];
   ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
@@ -497,10 +475,9 @@
 
   // Assert that the remainder of the file contains the incompressed data.
   std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
-  ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
-                                       entry.uncompressed_length));
-  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
-                      kATxtContents.size()));
+  ASSERT_TRUE(
+      android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
+  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
 
   // Assert that the total length of the file is sane
   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
@@ -517,7 +494,7 @@
 
   // Memory map the file first and open the archive from the memory region.
   android::FileMap file_map;
-  file_map.create(zip_path.c_str(), fd, 0/*offset*/, sb.st_size, true);
+  file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
                                      zip_path.c_str(), &handle));
@@ -533,9 +510,8 @@
 }
 #endif
 
-static void ZipArchiveStreamTest(
-    ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
-    bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
+static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
+                                 bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
   ZipString name;
   SetZipString(&name, entry_name);
   ASSERT_EQ(0, FindEntry(handle, name, entry));
@@ -564,9 +540,9 @@
   ASSERT_EQ(total_size, read_data->size());
 }
 
-static void ZipArchiveStreamTestUsingContents(
-    const std::string& zip_file, const std::string& entry_name,
-    const std::vector<uint8_t>& contents, bool raw) {
+static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
+                                              const std::string& entry_name,
+                                              const std::vector<uint8_t>& contents, bool raw) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
 
@@ -580,7 +556,8 @@
   CloseArchive(handle);
 }
 
-static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) {
+static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
+                                            const std::string& entry_name) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
 
@@ -642,6 +619,104 @@
   CloseArchive(handle);
 }
 
+// Generated using the following Java program:
+//     public static void main(String[] foo) throws Exception {
+//       FileOutputStream fos = new
+//       FileOutputStream("/tmp/data_descriptor.zip");
+//       ZipOutputStream zos = new ZipOutputStream(fos);
+//       ZipEntry ze = new ZipEntry("name");
+//       ze.setMethod(ZipEntry.DEFLATED);
+//       zos.putNextEntry(ze);
+//       zos.write("abdcdefghijk".getBytes());
+//       zos.closeEntry();
+//       zos.close();
+//     }
+//
+// cat /tmp/data_descriptor.zip | xxd -i
+//
+static const std::vector<uint8_t> kDataDescriptorZipFile{
+    0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
+    //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
+    0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+    0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
+    0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
+    0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
+    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// The offsets of the data descriptor in this file, so we can mess with
+// them later in the test.
+static constexpr uint32_t kDataDescriptorOffset = 48;
+static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
+static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
+
+static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
+                                 std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+
+  // This function expects a variant of kDataDescriptorZipFile, for look for
+  // an entry whose name is "name" and whose size is 12 (contents =
+  // "abdcdefghijk").
+  ZipEntry entry;
+  ZipString empty_name;
+  SetZipString(&empty_name, "name");
+
+  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+  ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+
+  entry_out->resize(12);
+  (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, ValidDataDescriptors) {
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
+
+  ASSERT_EQ(0, error_code);
+  ASSERT_EQ(12u, entry.size());
+  ASSERT_EQ('a', entry[0]);
+  ASSERT_EQ('k', entry[11]);
+}
+
+TEST(ziparchive, InvalidDataDescriptors) {
+  std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
+  invalid_csize[kCSizeOffset] = 0xfe;
+
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_EQ(kInconsistentInformation, error_code);
+
+  std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+  invalid_csize[kSizeOffset] = 0xfe;
+
+  error_code = 0;
+  entry.clear();
+  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+  ASSERT_EQ(kInconsistentInformation, error_code);
+}
+
+TEST(ziparchive, ErrorCodeString) {
+  ASSERT_STREQ("Success", ErrorCodeString(0));
+
+  // Out of bounds.
+  ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
+  ASSERT_STREQ("Unknown return code", ErrorCodeString(-13));
+
+  ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
+}
+
 // A zip file whose local file header at offset zero is corrupted.
 //
 // ---------------
@@ -694,10 +769,8 @@
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
-  static struct option options[] = {
-    { "test_data_dir", required_argument, nullptr, 't' },
-    { nullptr, 0, nullptr, 0 }
-  };
+  static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'},
+                                    {nullptr, 0, nullptr, 0}};
 
   while (true) {
     int option_index;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6d28bdb..6ad3366 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -16,11 +16,11 @@
 
 #include "ziparchive/zip_writer.h"
 
-#include <cstdio>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <zlib.h>
-#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+#include <cstdio>
+#define DEF_MEM_LEVEL 8  // normally in zutil.h?
 
 #include <memory>
 #include <vector>
@@ -33,13 +33,13 @@
 #include "zip_archive_common.h"
 
 #if !defined(powerof2)
-#define powerof2(x) ((((x)-1)&(x))==0)
+#define powerof2(x) ((((x)-1) & (x)) == 0)
 #endif
 
 /* Zip compression methods we support */
 enum {
-  kCompressStored     = 0,        // no compression
-  kCompressDeflated   = 8,        // standard deflate
+  kCompressStored = 0,    // no compression
+  kCompressDeflated = 8,  // standard deflate
 };
 
 // Size of the output buffer used for compression.
@@ -67,10 +67,7 @@
 static const int32_t kInvalidAlignment = -6;
 
 static const char* sErrorCodes[] = {
-    "Invalid state",
-    "IO error",
-    "Invalid entry name",
-    "Zlib error",
+    "Invalid state", "IO error", "Invalid entry name", "Zlib error",
 };
 
 const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -85,9 +82,13 @@
   delete stream;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
-                                state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
-                                buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f)
+    : file_(f),
+      seekable_(false),
+      current_offset_(0),
+      state_(State::kWritingZip),
+      z_stream_(nullptr, DeleteZStream),
+      buffer_(kBufSize) {
   // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
   // will fail as well.
   struct stat file_stats;
@@ -96,13 +97,14 @@
   }
 }
 
-ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
-                                           seekable_(writer.seekable_),
-                                           current_offset_(writer.current_offset_),
-                                           state_(writer.state_),
-                                           files_(std::move(writer.files_)),
-                                           z_stream_(std::move(writer.z_stream_)),
-                                           buffer_(std::move(writer.buffer_)){
+ZipWriter::ZipWriter(ZipWriter&& writer)
+    : file_(writer.file_),
+      seekable_(writer.seekable_),
+      current_offset_(writer.current_offset_),
+      state_(writer.state_),
+      files_(std::move(writer.files_)),
+      z_stream_(std::move(writer.z_stream_)),
+      buffer_(std::move(writer.buffer_)) {
   writer.file_ = nullptr;
   writer.state_ = State::kError;
 }
@@ -154,10 +156,10 @@
 
   struct tm* ptm;
 #if !defined(_WIN32)
-    struct tm tm_result;
-    ptm = localtime_r(&when, &tm_result);
+  struct tm tm_result;
+  ptm = localtime_r(&when, &tm_result);
 #else
-    ptm = localtime(&when);
+  ptm = localtime(&when);
 #endif
 
   int year = ptm->tm_year;
@@ -193,8 +195,8 @@
   dst->extra_field_length = src.padding_length;
 }
 
-int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
-                                             time_t time, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+                                             uint32_t alignment) {
   if (state_ != State::kWritingZip) {
     return kInvalidState;
   }
@@ -252,9 +254,8 @@
     return HandleError(kIoError);
   }
 
-  if (file_entry.padding_length != 0 &&
-      fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
-      != file_entry.padding_length) {
+  if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+                                               file_) != file_entry.padding_length) {
     return HandleError(kIoError);
   }
 
@@ -292,7 +293,7 @@
   CHECK(state_ == State::kWritingZip);
 
   // Initialize the z_stream for compression.
-  z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+  z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -331,8 +332,8 @@
     return result;
   }
 
-  current_file_entry_.crc32 = crc32(current_file_entry_.crc32,
-                                    reinterpret_cast<const Bytef*>(data), len);
+  current_file_entry_.crc32 =
+      crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
   current_file_entry_.uncompressed_size += len;
   return kNoError;
 }
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5b526a4..c284273 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "ziparchive/zip_archive.h"
 #include "ziparchive/zip_writer.h"
+#include "ziparchive/zip_archive.h"
 
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
@@ -135,17 +135,6 @@
   CloseArchive(handle);
 }
 
-static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
-  memset(tm, 0, sizeof(struct tm));
-  tm->tm_hour = (zip_time >> 11) & 0x1f;
-  tm->tm_min = (zip_time >> 5) & 0x3f;
-  tm->tm_sec = (zip_time & 0x1f) << 1;
-
-  tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
-  tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
-  tm->tm_mday = (zip_time >> 16) & 0x1f;
-}
-
 static struct tm MakeTm() {
   struct tm tm;
   memset(&tm, 0, sizeof(struct tm));
@@ -177,8 +166,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0x03);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -228,8 +216,7 @@
   ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
-  struct tm mod;
-  ConvertZipTimeToTm(data.mod_time, &mod);
+  struct tm mod = data.GetModificationTime();
   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
   EXPECT_EQ(tm.tm_min, mod.tm_min);
   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
@@ -374,7 +361,7 @@
 
   ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
   std::vector<uint8_t> data;
-  data.resize(1024*1024, 0xef);
+  data.resize(1024 * 1024, 0xef);
   ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
   ASSERT_EQ(0, writer.FinishEntry());
 
@@ -395,8 +382,9 @@
                                                             ZipArchiveHandle handle,
                                                             ZipEntry* zip_entry) {
   if (expected.size() != zip_entry->uncompressed_length) {
-    return ::testing::AssertionFailure() << "uncompressed entry size "
-        << zip_entry->uncompressed_length << " does not match expected size " << expected.size();
+    return ::testing::AssertionFailure()
+           << "uncompressed entry size " << zip_entry->uncompressed_length
+           << " does not match expected size " << expected.size();
   }
 
   std::string actual;
@@ -409,7 +397,7 @@
 
   if (expected != actual) {
     return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
-        << "' does not match expected '" << expected << "'";
+                                         << "' does not match expected '" << expected << "'";
   }
   return ::testing::AssertionSuccess();
 }
diff --git a/logcat/Android.bp b/logcat/Android.bp
new file mode 100644
index 0000000..729c8ff
--- /dev/null
+++ b/logcat/Android.bp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2006-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_defaults {
+    name: "logcat_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libpcrecpp",
+    ],
+    logtags: ["event.logtags"],
+}
+
+cc_library {
+    name: "liblogcat",
+
+    defaults: ["logcat_defaults"],
+    srcs: [
+        "logcat.cpp",
+        "getopt_long.cpp",
+        "logcat_system.cpp",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_binary {
+    name: "logcat",
+
+    defaults: ["logcat_defaults"],
+    shared_libs: ["liblogcat"],
+    srcs: [
+        "logcat_main.cpp",
+    ],
+}
+
+cc_binary {
+    name: "logcatd",
+
+    defaults: ["logcat_defaults"],
+    shared_libs: ["liblogcat"],
+    srcs: [
+        "logcatd_main.cpp",
+    ],
+}
+
+cc_prebuilt_binary {
+    name: "logpersist.start",
+    srcs: ["logpersist"],
+    init_rc: ["logcatd.rc"],
+    symlinks: ["logpersist.stop", "logpersist.cat"],
+    strip: {
+        none: true,
+    }
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 4e11ca9..a716993 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -2,48 +2,4 @@
 
 LOCAL_PATH := $(call my-dir)
 
-logcatLibs := liblog libbase libcutils libpcrecpp
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcat
-LOCAL_SRC_FILES := logcat_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcatd
-LOCAL_MODULE_TAGS := debug
-LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := liblogcat
-LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
-LOCAL_SHARED_LIBRARIES := $(logcatLibs)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logpersist.start
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_INIT_RC := logcatd.rc
-LOCAL_MODULE_PATH := $(bin_dir)
-LOCAL_SRC_FILES := logpersist
-ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_PREBUILT)
-
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
index 5f8dd66..da99906 100644
--- a/logcat/getopt_long.cpp
+++ b/logcat/getopt_long.cpp
@@ -49,7 +49,6 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index e9ef9cc..3d56472 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1019,7 +1019,6 @@
                 break;
 
             case 'm': {
-                char* end = nullptr;
                 if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
                     logcat_panic(context, HELP_FALSE,
                                  "-%c \"%s\" isn't an "
@@ -1182,7 +1181,6 @@
                 std::unique_ptr<char, void (*)(void*)> formats(
                     strdup(optctx.optarg), free);
                 char* arg = formats.get();
-                unsigned idMask = 0;
                 char* sv = nullptr;  // protect against -ENOMEM above
                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
                     err = setLogFormat(context, arg);
@@ -1256,7 +1254,7 @@
                         // example: "qemu_pipe,pipe:logcat"
                         // upon opening of /dev/qemu_pipe, the "pipe:logcat"
                         // string with trailing '\0' should be written to the fd
-                        size_t pos = devname.find(",");
+                        size_t pos = devname.find(',');
                         if (pos != std::string::npos) {
                             pipePurpose = devname.substr(pos + 1);
                             devname = devname.substr(0, pos);
@@ -1638,7 +1636,7 @@
                 logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
                 break;
             }
-            logcat_panic(context, HELP_FALSE, "logcat read failure");
+            logcat_panic(context, HELP_FALSE, "logcat read failure\n");
             break;
         }
 
@@ -1733,7 +1731,7 @@
     pthread_attr_setschedparam(&attr, &param);
     pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
     if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
-        int save_errno = errno;
+        save_errno = errno;
         goto pthread_attr_exit;
     }
 
@@ -1773,7 +1771,7 @@
     context->retval = EXIT_SUCCESS;
     if (pthread_create(&context->thr, &attr,
                        (void*(*)(void*))__logcat, context)) {
-        int save_errno = errno;
+        save_errno = errno;
         goto argv_exit;
     }
     pthread_attr_destroy(&attr);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
index 9e9a2c2..c8a00da 100644
--- a/logcat/tests/liblogcat_test.cpp
+++ b/logcat/tests/liblogcat_test.cpp
@@ -17,8 +17,8 @@
 #include <log/logcat.h>
 
 #define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&context, command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
+#define logcat_popen(context, command) android_logcat_popen(&(context), command)
+#define logcat_pclose(context, fp) android_logcat_pclose(&(context), fp)
 #define logcat_system(command) android_logcat_system(command)
 #define logcat liblogcat
 
diff --git a/logd/Android.bp b/logd/Android.bp
new file mode 100644
index 0000000..68b79d3
--- /dev/null
+++ b/logd/Android.bp
@@ -0,0 +1,78 @@
+// 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.
+
+// This is what we want to do:
+//  event_logtags = $(shell
+//    sed -n
+//        "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p"
+//        $(LOCAL_PATH)/$2/event.logtags)
+//  event_flag := $(call event_logtags,auditd)
+//  event_flag += $(call event_logtags,logd)
+//  event_flag += $(call event_logtags,tag_def)
+// so make sure we do not regret hard-coding it as follows:
+event_flag = [
+    "-DAUDITD_LOG_TAG=1003",
+    "-DCHATTY_LOG_TAG=1004",
+    "-DTAG_DEF_LOG_TAG=1005",
+    "-DLIBLOG_LOG_TAG=1006"
+]
+
+cc_library_static {
+    name: "liblogd",
+
+    srcs: [
+        "LogCommand.cpp",
+        "CommandListener.cpp",
+        "LogListener.cpp",
+        "LogReader.cpp",
+        "FlushCommand.cpp",
+        "LogBuffer.cpp",
+        "LogBufferElement.cpp",
+        "LogBufferInterface.cpp",
+        "LogTimes.cpp",
+        "LogStatistics.cpp",
+        "LogWhiteBlackList.cpp",
+        "libaudit.c",
+        "LogAudit.cpp",
+        "LogKlog.cpp",
+        "LogTags.cpp",
+    ],
+    logtags: ["event.logtags"],
+
+    shared_libs: ["libbase"],
+
+    export_include_dirs: ["."],
+
+    cflags: ["-Werror"] + event_flag,
+}
+
+cc_binary {
+    name: "logd",
+    init_rc: ["logd.rc"],
+
+    srcs: ["main.cpp"],
+
+    static_libs: ["liblogd"],
+
+    shared_libs: [
+        "libsysutils",
+        "liblog",
+        "libcutils",
+        "libbase",
+        "libpackagelistparser",
+        "libcap",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index fb51992..1bca891 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -2,73 +2,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE:= liblogd
-
-LOCAL_SRC_FILES := \
-    LogCommand.cpp \
-    CommandListener.cpp \
-    LogListener.cpp \
-    LogReader.cpp \
-    FlushCommand.cpp \
-    LogBuffer.cpp \
-    LogBufferElement.cpp \
-    LogBufferInterface.cpp \
-    LogTimes.cpp \
-    LogStatistics.cpp \
-    LogWhiteBlackList.cpp \
-    libaudit.c \
-    LogAudit.cpp \
-    LogKlog.cpp \
-    LogTags.cpp \
-    event.logtags
-
-LOCAL_SHARED_LIBRARIES := \
-    libbase
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-# This is what we want to do:
-#  event_logtags = $(shell \
-#    sed -n \
-#        "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" \
-#        $(LOCAL_PATH)/$2/event.logtags)
-#  event_flag := $(call event_logtags,auditd)
-#  event_flag += $(call event_logtags,logd)
-#  event_flag += $(call event_logtags,tag_def)
-# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
-event_flag += -DLIBLOG_LOG_TAG=1006
-
-LOCAL_CFLAGS := -Werror $(event_flag)
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= logd
-
-LOCAL_INIT_RC := logd.rc
-
-LOCAL_SRC_FILES := \
-    main.cpp
-
-LOCAL_STATIC_LIBRARIES := \
-    liblogd
-
-LOCAL_SHARED_LIBRARIES := \
-    libsysutils \
-    liblog \
-    libcutils \
-    libbase \
-    libpackagelistparser \
-    libcap
-
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
 LOCAL_MODULE := logtagd.rc
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index ee38d1c..cfcbaa5 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -25,7 +25,11 @@
 #include <sys/uio.h>
 #include <syslog.h>
 
+#include <fstream>
+#include <sstream>
+
 #include <android-base/macros.h>
+#include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
@@ -138,6 +142,71 @@
     return true;
 }
 
+static inline bool hasMetadata(char* str, int str_len) {
+    // need to check and see if str already contains bug metadata from
+    // possibility of stuttering if log audit crashes and then reloads kernel
+    // messages. Kernel denials that contain metadata will either end in
+    // "b/[0-9]+$" or "b/[0-9]+  duplicate messages suppressed$" which will put
+    // a '/' character at either 9 or 39 indices away from the end of the str.
+    return str_len >= 39 &&
+           (str[str_len - 9] == '/' || str[str_len - 39] == '/');
+}
+
+std::map<std::string, std::string> LogAudit::populateDenialMap() {
+    std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
+    std::string line;
+    // allocate a map for the static map pointer in logParse to keep track of,
+    // this function only runs once
+    std::map<std::string, std::string> denial_to_bug;
+    if (bug_file.good()) {
+        std::string scontext;
+        std::string tcontext;
+        std::string tclass;
+        std::string bug_num;
+        while (std::getline(bug_file, line)) {
+            std::stringstream split_line(line);
+            split_line >> scontext >> tcontext >> tclass >> bug_num;
+            denial_to_bug.emplace(scontext + tcontext + tclass, bug_num);
+        }
+    }
+    return denial_to_bug;
+}
+
+std::string LogAudit::denialParse(const std::string& denial, char terminator,
+                                  const std::string& search_term) {
+    size_t start_index = denial.find(search_term);
+    if (start_index != std::string::npos) {
+        start_index += search_term.length();
+        return denial.substr(
+            start_index, denial.find(terminator, start_index) - start_index);
+    }
+    return "";
+}
+
+void LogAudit::logParse(const std::string& string, std::string* bug_num) {
+    if (!__android_log_is_debuggable()) {
+        bug_num->assign("");
+        return;
+    }
+    static std::map<std::string, std::string> denial_to_bug =
+        populateDenialMap();
+    std::string scontext = denialParse(string, ':', "scontext=u:object_r:");
+    std::string tcontext = denialParse(string, ':', "tcontext=u:object_r:");
+    std::string tclass = denialParse(string, ' ', "tclass=");
+    if (scontext.empty()) {
+        scontext = denialParse(string, ':', "scontext=u:r:");
+    }
+    if (tcontext.empty()) {
+        tcontext = denialParse(string, ':', "tcontext=u:r:");
+    }
+    auto search = denial_to_bug.find(scontext + tcontext + tclass);
+    if (search != denial_to_bug.end()) {
+        bug_num->assign(" b/" + search->second);
+    } else {
+        bug_num->assign("");
+    }
+}
+
 int LogAudit::logPrint(const char* fmt, ...) {
     if (fmt == NULL) {
         return -EINVAL;
@@ -153,7 +222,6 @@
     if (rc < 0) {
         return rc;
     }
-
     char* cp;
     // Work around kernels missing
     // https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
@@ -165,10 +233,10 @@
     while ((cp = strstr(str, "  "))) {
         memmove(cp, cp + 1, strlen(cp + 1) + 1);
     }
-
     bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+    static std::string bug_metadata;
     if ((fdDmesg >= 0) && initialized) {
-        struct iovec iov[3];
+        struct iovec iov[4];
         static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
         static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
         static const char newline[] = "\n";
@@ -197,19 +265,20 @@
             }
             if (!skip) {
                 static const char resume[] = " duplicate messages suppressed\n";
-
                 iov[0].iov_base = last_info ? const_cast<char*>(log_info)
                                             : const_cast<char*>(log_warning);
                 iov[0].iov_len =
                     last_info ? sizeof(log_info) : sizeof(log_warning);
                 iov[1].iov_base = last_str;
                 iov[1].iov_len = strlen(last_str);
+                iov[2].iov_base = const_cast<char*>(bug_metadata.c_str());
+                iov[2].iov_len = bug_metadata.length();
                 if (count > 1) {
-                    iov[2].iov_base = const_cast<char*>(resume);
-                    iov[2].iov_len = strlen(resume);
+                    iov[3].iov_base = const_cast<char*>(resume);
+                    iov[3].iov_len = strlen(resume);
                 } else {
-                    iov[2].iov_base = const_cast<char*>(newline);
-                    iov[2].iov_len = strlen(newline);
+                    iov[3].iov_base = const_cast<char*>(newline);
+                    iov[3].iov_len = strlen(newline);
                 }
 
                 writev(fdDmesg, iov, arraysize(iov));
@@ -223,13 +292,16 @@
             last_info = info;
         }
         if (count == 0) {
+            logParse(str, &bug_metadata);
             iov[0].iov_base = info ? const_cast<char*>(log_info)
                                    : const_cast<char*>(log_warning);
             iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
             iov[1].iov_base = str;
             iov[1].iov_len = strlen(str);
-            iov[2].iov_base = const_cast<char*>(newline);
-            iov[2].iov_len = strlen(newline);
+            iov[2].iov_base = const_cast<char*>(bug_metadata.c_str());
+            iov[2].iov_len = bug_metadata.length();
+            iov[3].iov_base = const_cast<char*>(newline);
+            iov[3].iov_len = strlen(newline);
 
             writev(fdDmesg, iov, arraysize(iov));
         }
@@ -285,24 +357,32 @@
 
     // log to events
 
-    size_t l = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
-    size_t n = l + sizeof(android_log_event_string_t);
+    size_t str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
+    if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
+        logParse(str, &bug_metadata);
+    str_len = (str_len + bug_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
+                  ? str_len + bug_metadata.length()
+                  : LOGGER_ENTRY_MAX_PAYLOAD;
+    size_t message_len = str_len + sizeof(android_log_event_string_t);
 
     bool notify = false;
 
     if (events) {  // begin scope for event buffer
-        uint32_t buffer[(n + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
+        uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
 
         android_log_event_string_t* event =
             reinterpret_cast<android_log_event_string_t*>(buffer);
         event->header.tag = htole32(AUDITD_LOG_TAG);
         event->type = EVENT_TYPE_STRING;
-        event->length = htole32(l);
-        memcpy(event->data, str, l);
+        event->length = htole32(message_len);
+        memcpy(event->data, str, str_len - bug_metadata.length());
+        memcpy(event->data + str_len - bug_metadata.length(),
+               bug_metadata.c_str(), bug_metadata.length());
 
-        rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid,
-                         reinterpret_cast<char*>(event),
-                         (n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+        rc = logbuf->log(
+            LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+            (message_len <= USHRT_MAX) ? (unsigned short)message_len
+                                       : USHRT_MAX);
         if (rc >= 0) {
             notify = true;
         }
@@ -333,28 +413,31 @@
     const char* ecomm = strchr(comm, '"');
     if (ecomm) {
         ++ecomm;
-        l = ecomm - comm;
+        str_len = ecomm - comm;
     } else {
-        l = strlen(comm) + 1;
+        str_len = strlen(comm) + 1;
         ecomm = "";
     }
-    size_t b = estr - str;
-    if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
-        b = LOGGER_ENTRY_MAX_PAYLOAD;
+    size_t prefix_len = estr - str;
+    if (prefix_len > LOGGER_ENTRY_MAX_PAYLOAD) {
+        prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
     }
-    size_t e = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - b);
-    n = b + e + l + 2;
+    size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
+    message_len = str_len + prefix_len + suffix_len + bug_metadata.length() + 2;
 
     if (main) {  // begin scope for main buffer
-        char newstr[n];
+        char newstr[message_len];
 
         *newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
-        strlcpy(newstr + 1, comm, l);
-        strncpy(newstr + 1 + l, str, b);
-        strncpy(newstr + 1 + l + b, ecomm, e);
+        strlcpy(newstr + 1, comm, str_len);
+        strncpy(newstr + 1 + str_len, str, prefix_len);
+        strncpy(newstr + 1 + str_len + prefix_len, ecomm, suffix_len);
+        strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
+                bug_metadata.c_str(), bug_metadata.length());
 
         rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
-                         (n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+                         (message_len <= USHRT_MAX) ? (unsigned short)message_len
+                                                    : USHRT_MAX);
 
         if (rc >= 0) {
             notify = true;
@@ -368,7 +451,7 @@
     if (notify) {
         reader->notifyNewLog();
         if (rc < 0) {
-            rc = n;
+            rc = message_len;
         }
     }
 
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 5947819..2bd02d4 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -17,6 +17,7 @@
 #ifndef _LOGD_LOG_AUDIT_H__
 #define _LOGD_LOG_AUDIT_H__
 
+#include <map>
 #include <queue>
 
 #include <sysutils/SocketListener.h>
@@ -50,6 +51,10 @@
 
    private:
     static int getLogSocket();
+    std::map<std::string, std::string> populateDenialMap();
+    std::string denialParse(const std::string& denial, char terminator,
+                            const std::string& search_term);
+    void logParse(const std::string& string, std::string* bug_num);
     int logPrint(const char* fmt, ...)
         __attribute__((__format__(__printf__, 2, 3)));
 };
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 381c974..f20ac45 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -41,22 +41,20 @@
       mTid(tid),
       mRealTime(realtime),
       mMsgLen(len),
-      mLogId(log_id) {
+      mLogId(log_id),
+      mDropped(false) {
     mMsg = new char[len];
     memcpy(mMsg, msg, len);
-    mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t)))
-               ? le32toh(reinterpret_cast<android_event_header_t*>(mMsg)->tag)
-               : 0;
 }
 
 LogBufferElement::LogBufferElement(const LogBufferElement& elem)
-    : mTag(elem.mTag),
-      mUid(elem.mUid),
+    : mUid(elem.mUid),
       mPid(elem.mPid),
       mTid(elem.mTid),
       mRealTime(elem.mRealTime),
       mMsgLen(elem.mMsgLen),
-      mLogId(elem.mLogId) {
+      mLogId(elem.mLogId),
+      mDropped(elem.mDropped) {
     mMsg = new char[mMsgLen];
     memcpy(mMsg, elem.mMsg, mMsgLen);
 }
@@ -65,6 +63,32 @@
     delete[] mMsg;
 }
 
+uint32_t LogBufferElement::getTag() const {
+    return (isBinary() &&
+            ((mDropped && mMsg != nullptr) ||
+             (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
+               ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
+               : 0;
+}
+
+unsigned short LogBufferElement::setDropped(unsigned short value) {
+    // The tag information is saved in mMsg data, if the tag is non-zero
+    // save only the information needed to get the tag.
+    if (getTag() != 0) {
+        if (mMsgLen > sizeof(android_event_header_t)) {
+            char* truncated_msg = new char[sizeof(android_event_header_t)];
+            memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
+            delete[] mMsg;
+            mMsg = truncated_msg;
+        }  // mMsgLen == sizeof(android_event_header_t), already at minimum.
+    } else {
+        delete[] mMsg;
+        mMsg = nullptr;
+    }
+    mDropped = true;
+    return mDroppedCount = value;
+}
+
 // caller must own and free character string
 char* android::tidToName(pid_t tid) {
     char* retval = NULL;
@@ -164,8 +188,8 @@
     // identical to below to calculate the buffer size required
     const char* type = lastSame ? "identical" : "expire";
     size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
-                          commName ? commName : "", type, mDropped,
-                          (mDropped > 1) ? "s" : "");
+                          commName ? commName : "", type, getDropped(),
+                          (getDropped() > 1) ? "s" : "");
 
     size_t hdrLen;
     if (isBinary()) {
@@ -196,8 +220,8 @@
     }
 
     snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
-             commName ? commName : "", type, mDropped,
-             (mDropped > 1) ? "s" : "");
+             commName ? commName : "", type, getDropped(),
+             (getDropped() > 1) ? "s" : "");
     free(const_cast<char*>(name));
     free(const_cast<char*>(commName));
 
@@ -225,7 +249,7 @@
 
     char* buffer = NULL;
 
-    if (!mMsg) {
+    if (mDropped) {
         entry.len = populateDroppedMessage(buffer, parent, lastSame);
         if (!entry.len) return mRealTime;
         iovec[1].iov_base = buffer;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 814ec87..b168645 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -32,25 +32,25 @@
                                   // chatty for the temporal expire messages
 #define EXPIRE_RATELIMIT 10  // maximum rate in seconds to report expiration
 
-class LogBufferElement {
+class __attribute__((packed)) LogBufferElement {
     friend LogBuffer;
 
     // sized to match reality of incoming log packets
-    uint32_t mTag;  // only valid for isBinary()
     const uint32_t mUid;
     const uint32_t mPid;
     const uint32_t mTid;
     log_time mRealTime;
     char* mMsg;
     union {
-        const uint16_t mMsgLen;  // mMSg != NULL
-        uint16_t mDropped;       // mMsg == NULL
+        const uint16_t mMsgLen;  // mDropped == false
+        uint16_t mDroppedCount;  // mDropped == true
     };
     const uint8_t mLogId;
+    bool mDropped;
 
     static atomic_int_fast64_t sequence;
 
-    // assumption: mMsg == NULL
+    // assumption: mDropped == true
     size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
                                   bool lastSame);
 
@@ -58,7 +58,7 @@
     LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
                      pid_t tid, const char* msg, unsigned short len);
     LogBufferElement(const LogBufferElement& elem);
-    virtual ~LogBufferElement();
+    ~LogBufferElement();
 
     bool isBinary(void) const {
         return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
@@ -76,24 +76,16 @@
     pid_t getTid(void) const {
         return mTid;
     }
-    uint32_t getTag() const {
-        return mTag;
-    }
+    uint32_t getTag() const;
     unsigned short getDropped(void) const {
-        return mMsg ? 0 : mDropped;
+        return mDropped ? mDroppedCount : 0;
     }
-    unsigned short setDropped(unsigned short value) {
-        if (mMsg) {
-            delete[] mMsg;
-            mMsg = NULL;
-        }
-        return mDropped = value;
-    }
+    unsigned short setDropped(unsigned short value);
     unsigned short getMsgLen() const {
-        return mMsg ? mMsgLen : 0;
+        return mDropped ? 0 : mMsgLen;
     }
     const char* getMsg() const {
-        return mMsg;
+        return mDropped ? nullptr : mMsg;
     }
     log_time getRealTime(void) const {
         return mRealTime;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index cd80212..9e1541b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -646,16 +646,20 @@
                 recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         }
 
-        alarm_timeout =
-            alarm((old_alarm <= 0) ? old_alarm
-                                   : (old_alarm > (1 + 3 - alarm_wrap))
-                                         ? old_alarm - 3 + alarm_wrap
-                                         : 2);
+        if (old_alarm > 0) {
+            unsigned int time_spent = 3 - alarm_wrap;
+            if (old_alarm > time_spent + 1) {
+                old_alarm -= time_spent;
+            } else {
+                old_alarm = 2;
+            }
+        }
+        alarm_timeout = alarm(old_alarm);
         sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
 
-        if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+        if (content_wrap && alarm_wrap && content_timeout && alarm_timeout) {
             break;
         }
     }
@@ -710,8 +714,8 @@
     // A few tries to get it right just in case wrap kicks in due to
     // content providers being active during the test.
     int i = 5;
-    log_time now(android_log_clockid());
-    now.tv_sec -= 30;  // reach back a moderate period of time
+    log_time start(android_log_clockid());
+    start.tv_sec -= 30;  // reach back a moderate period of time
 
     while (--i) {
         int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -726,7 +730,7 @@
         std::string ask = android::base::StringPrintf(
             "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
             ".%09" PRIu32,
-            now.tv_sec, now.tv_nsec);
+            start.tv_sec, start.tv_nsec);
 
         struct sigaction ignore, old_sigaction;
         memset(&ignore, 0, sizeof(ignore));
@@ -756,11 +760,15 @@
                 recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         }
 
-        alarm_timeout =
-            alarm((old_alarm <= 0) ? old_alarm
-                                   : (old_alarm > (1 + 3 - alarm_wrap))
-                                         ? old_alarm - 3 + alarm_wrap
-                                         : 2);
+        if (old_alarm > 0) {
+            unsigned int time_spent = 3 - alarm_wrap;
+            if (old_alarm > time_spent + 1) {
+                old_alarm -= time_spent;
+            } else {
+                old_alarm = 2;
+            }
+        }
+        alarm_timeout = alarm(old_alarm);
         sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
@@ -773,23 +781,23 @@
         // active _or_ inactive during the test.
         if (content_timeout) {
             log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
-            if (msg < now) {
+            if (msg < start) {
                 fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
-                        msg_timeout.entry.nsec, (unsigned)now.tv_sec,
-                        (unsigned)now.tv_nsec);
+                        msg_timeout.entry.nsec, (unsigned)start.tv_sec,
+                        (unsigned)start.tv_nsec);
                 _exit(-1);
             }
-            if (msg > now) {
-                now = msg;
-                now.tv_sec += 30;
-                msg = log_time(android_log_clockid());
-                if (now > msg) {
-                    now = msg;
-                    --now.tv_sec;
+            if (msg > start) {
+                start = msg;
+                start.tv_sec += 30;
+                log_time now = log_time(android_log_clockid());
+                if (start > now) {
+                    start = now;
+                    --start.tv_sec;
                 }
             }
         } else {
-            now.tv_sec -= 120;  // inactive, reach further back!
+            start.tv_sec -= 120;  // inactive, reach further back!
         }
     }
 
@@ -802,8 +810,8 @@
     }
 
     if (content_wrap || !content_timeout) {
-        fprintf(stderr, "now=%" PRIu32 ".%09" PRIu32 "\n", now.tv_sec,
-                now.tv_nsec);
+        fprintf(stderr, "start=%" PRIu32 ".%09" PRIu32 "\n", start.tv_sec,
+                start.tv_nsec);
     }
 
     EXPECT_TRUE(written);
@@ -933,8 +941,12 @@
 }
 
 #ifdef __ANDROID__
-static inline int32_t get4LE(const char* src) {
-    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static inline uint32_t get4LE(const char* src) {
+  return get4LE(reinterpret_cast<const uint8_t*>(src));
 }
 #endif
 
@@ -1087,7 +1099,7 @@
     // and dac_read_search on every try to get past the message
     // de-duper.  We will also rotate the file name in the directory
     // as another measure.
-    static const char file[] = "/data/backup/cannot_access_directory_%u";
+    static const char file[] = "/data/drm/cannot_access_directory_%u";
     static const unsigned avc_requests_per_access = 2;
 
     rate /= avc_requests_per_access;
diff --git a/reboot/reboot.c b/reboot/reboot.c
index 007dfba..fe763a8 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -21,13 +21,13 @@
 #include <cutils/android_reboot.h>
 #include <unistd.h>
 
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
     int ret;
     size_t prop_len;
     char property_val[PROPERTY_VALUE_MAX];
-    const char *cmd = "reboot";
-    char *optarg = "";
+    static const char reboot[] = "reboot";
+    const char* cmd = reboot;
+    char* optarg = "";
 
     opterr = 0;
     do {
@@ -56,22 +56,27 @@
 
     if (argc > optind)
         optarg = argv[optind];
+    if (!optarg || !optarg[0]) optarg = "shell";
 
     prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
     if (prop_len >= sizeof(property_val)) {
-        fprintf(stderr, "reboot command too long: %s\n", optarg);
+        fprintf(stderr, "%s command too long: %s\n", cmd, optarg);
         exit(EXIT_FAILURE);
     }
 
     ret = property_set(ANDROID_RB_PROPERTY, property_val);
-    if(ret < 0) {
-        perror("reboot");
+    if (ret < 0) {
+        perror(cmd);
         exit(EXIT_FAILURE);
     }
 
     // Don't return early. Give the reboot command time to take effect
     // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
-    while(1) { pause(); }
+    if (cmd == reboot) {
+        while (1) {
+            pause();
+        }
+    }
 
     fprintf(stderr, "Done\n");
     return 0;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 2e5575f..1bac36c 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -142,7 +142,7 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data oem acct config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    sbin dev proc sys system data oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
     ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
diff --git a/rootdir/asan.options b/rootdir/asan.options
index d728f12..a264d2d 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -5,3 +5,4 @@
 detect_container_overflow=0
 abort_on_error=1
 include_if_exists=/system/asan.options.%b
+include_if_exists=/data/asan/system/asan.options.%b
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index bcdecf2..c5e149c 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,3 +1,4 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
 libc.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index f4da09d..a4ca683 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,3 +1,4 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
 libc.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index fd2c838..917e99d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -36,6 +36,8 @@
     mount cgroup none /dev/memcg memory
     # app mem cgroups, used by activity manager, lmkd and zygote
     mkdir /dev/memcg/apps/ 0755 system system
+    # cgroup for system_server and surfaceflinger
+    mkdir /dev/memcg/system 0550 system system
 
     start ueventd
 
@@ -225,6 +227,8 @@
     mount pstore pstore /sys/fs/pstore
     chown system log /sys/fs/pstore/console-ramoops
     chmod 0440 /sys/fs/pstore/console-ramoops
+    chown system log /sys/fs/pstore/console-ramoops-0
+    chmod 0440 /sys/fs/pstore/console-ramoops-0
     chown system log /sys/fs/pstore/pmsg-ramoops-0
     chmod 0440 /sys/fs/pstore/pmsg-ramoops-0
 
@@ -238,7 +242,10 @@
     export DOWNLOAD_CACHE /data/cache
 
     # set RLIMIT_NICE to allow priorities from 19 to -20
-    setrlimit 13 40 40
+    setrlimit nice 40 40
+
+    # Allow up to 32K FDs per process
+    setrlimit nofile 32768 32768
 
     # This allows the ledtrig-transient properties to be created here so
     # that they can be chown'd to system:system later on boot
@@ -317,7 +324,6 @@
     # Make sure /sys/kernel/debug (if present) is labeled properly
     # Note that tracefs may be mounted under debug, so we need to cross filesystems
     restorecon --recursive --cross-filesystems /sys/kernel/debug
-    chmod 0755 /sys/kernel/debug/tracing
 
     # We chown/chmod /cache again so because mount is run as root + defaults
     chown system cache /cache
@@ -355,6 +361,10 @@
     mkdir /cache/lost+found 0770 root root
 
 on late-fs
+    # Ensure that tracefs has the correct permissions.
+    # This does not work correctly if it is called in post-fs.
+    chmod 0755 /sys/kernel/debug/tracing
+
     # HALs required before storage encryption can get unlocked (FBE/FDE)
     class_start early_hal
 
@@ -427,7 +437,6 @@
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
     mkdir /data/misc/trace 0700 root root
-    mkdir /data/misc/reboot 0700 system system
     # profile file layout
     mkdir /data/misc/profiles 0771 system system
     mkdir /data/misc/profiles/cur 0771 system system
@@ -716,6 +725,7 @@
     user shell
     group shell log readproc
     seclabel u:r:shell:s0
+    setenv HOSTNAME console
 
 on property:ro.debuggable=1
     # Give writes to anyone for the trace folder on debug builds.
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 915d159..3168f40 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -12,7 +12,7 @@
     mkdir /data/adb 0700 root root
 
 # adbd is controlled via property triggers in init.<platform>.usb.rc
-service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
     class core
     socket adbd stream 660 system system
     disabled
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index e7b8cc2..b27cfad 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -194,6 +194,7 @@
   ScopedMinijail j(minijail_new());
   minijail_change_uid(j.get(), uid);
   minijail_change_gid(j.get(), gid);
+  minijail_keep_supplementary_gids(j.get());
   minijail_enter(j.get());
 
   if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index 0c58574..5b4dc58 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -8,6 +8,5 @@
 LOCAL_SHARED_LIBRARIES := libbase libcutils libminijail libpackagelistparser
 
 LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
 
 include $(BUILD_EXECUTABLE)
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index c342cf8..343a903 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -249,7 +249,9 @@
     global.root.uid = AID_ROOT;
     global.root.under_android = false;
 
-    strcpy(global.source_path, source_path);
+    // Clang static analyzer think strcpy potentially overwrites other fields
+    // in global. Use snprintf() to mute the false warning.
+    snprintf(global.source_path, sizeof(global.source_path), "%s", source_path);
 
     if (multi_user) {
         global.root.perm = PERM_PRE_ROOT;
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 6d35fed..9620d63 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -4,7 +4,6 @@
         "bzip2",
         "grep",
         "grep_vendor",
-        "gzip",
         "mkshrc",
         "mkshrc_vendor",
         "reboot",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 5d10c18..c4e8aac 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -4,20 +4,25 @@
 Since IceCreamSandwich Android has used
 [mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
 [ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
-remained in the tree up to and including KitKat).
+remained unused in the tree up to and including KitKat).
 
-Initially Android had a very limited command-line provided by its
-own "toolbox" binary. These days almost everything is supplied by
+Initially Android had a very limited command-line provided by its own
+"toolbox" binary. Since Marshmallow almost everything is supplied by
 [toybox](http://landley.net/toybox/) instead.
 
 We started moving a few of the more important tools to full
-BSD implementations in JellyBean before we started in earnest in
+BSD implementations in JellyBean, and continued this work in
 Lollipop. Lollipop was a major break with the past in many ways (LP64
 support and the switch to ART both having lots of knock-on effects around
 the system), so although this was the beginning of the end of toolbox it
 (a) didn't stand out given all the other systems-level changes and (b)
 in Marshmallow we changed direction and started the move to toybox.
 
+Not everything is provided by toybox, though. We currently still use
+the BSD dd and grep (because the toybox versions are still unfinished),
+and for the bzip2 command-line tools we use the ones that are part of
+the bzip2 distribution.
+
 The lists below show what tools were provided and where they came from in
 each release starting with Gingerbread. This doesn't tell the full story,
 because the toolbox implementations did have bugs fixed and options added
@@ -25,6 +30,10 @@
 `-f`. But this gives you an idea of what was available in any given release,
 and how usable it was likely to be.
 
+Also note that in any given release `toybox` probably contains more
+commands than there are symlinks for in `/system/bin`. You can get the
+full list for a release by running `toybox` directly.
+
 
 Android 2.3 (Gingerbread)
 -------------------------
@@ -132,26 +141,26 @@
 uptime usleep vmstat wc which whoami xargs xxd yes
 
 
-Current AOSP
-------------
+Android 8.0 (Oreo)
+------------------
 
 BSD: dd grep
 
 bzip2: bzcat bzip2 bunzip2
 
-toolbox: getevent gzip newfs\_msdos gunzip zcat
+toolbox: getevent newfs\_msdos
 
 toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
 chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
 dos2unix du echo env expand expr fallocate false file find flock free
-getenforce getprop groups head hostname hwclock id ifconfig inotifyd
-insmod ionice iorenice kill killall ln load\_policy log logname losetup
-ls lsmod lsof lsusb md5sum microcom mkdir mknod mkswap mktemp modinfo
-modprobe more mount mountpoint mv netstat nice nl nohup od paste patch
-pgrep pidof pkill pmap printenv printf ps pwd readlink realpath renice
-restorecon rm rmdir rmmod runcon sed sendevent seq setenforce setprop
-setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
-start stat stop strings swapoff swapon sync sysctl tac tail tar taskset
-tee time timeout top touch tr true truncate tty ulimit umount uname uniq
-unix2dos uptime usleep uudecode uuencode vmstat wc which whoami xargs
-xxd yes
+getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf ps pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed sendevent
+seq setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout top touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
+vmstat wc which whoami xargs xxd yes zcat
diff --git a/toolbox/upstream-netbsd/include/sys/mtio.h b/toolbox/upstream-netbsd/include/sys/mtio.h
deleted file mode 100644
index 8fb5655..0000000
--- a/toolbox/upstream-netbsd/include/sys/mtio.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <linux/mtio.h>
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index b38eb05..d63757b 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -24,7 +24,8 @@
 // Commands
 enum keymaster_command : uint32_t {
     KEYMASTER_RESP_BIT              = 1,
-    KEYMASTER_REQ_SHIFT             = 1,
+    KEYMASTER_STOP_BIT              = 2,
+    KEYMASTER_REQ_SHIFT             = 2,
 
     KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),
     KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index 5f16fd0..55a03bd 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -36,7 +36,8 @@
 #include "trusty_keymaster_device.h"
 #include "trusty_keymaster_ipc.h"
 
-const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+// Maximum size of message from Trusty is 8K (for RSA attestation key and chain)
+const uint32_t RECV_BUF_SIZE = 2*PAGE_SIZE;
 const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
 
 const size_t kMaximumAttestationChallengeLength = 128;
@@ -176,14 +177,14 @@
     }
 
     AuthorizationSet params_copy(*params);
-    ConfigureRequest request;
+    ConfigureRequest request(message_version_);
     if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
         !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
         ALOGD("Configuration parameters must contain OS version and patch level");
         return KM_ERROR_INVALID_ARGUMENT;
     }
 
-    ConfigureResponse response;
+    ConfigureResponse response(message_version_);
     keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -199,9 +200,9 @@
         return error_;
     }
 
-    AddEntropyRequest request;
+    AddEntropyRequest request(message_version_);
     request.random_data.Reinitialize(data, data_length);
-    AddEntropyResponse response;
+    AddEntropyResponse response(message_version_);
     return Send(KM_ADD_RNG_ENTROPY, request, &response);
 }
 
@@ -260,11 +261,11 @@
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
     }
 
-    GetKeyCharacteristicsRequest request;
+    GetKeyCharacteristicsRequest request(message_version_);
     request.SetKeyMaterial(*key_blob);
     AddClientAndAppData(client_id, app_data, &request);
 
-    GetKeyCharacteristicsResponse response;
+    GetKeyCharacteristicsResponse response(message_version_);
     keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -378,7 +379,7 @@
     cert_chain->entry_count = 0;
     cert_chain->entries = nullptr;
 
-    AttestKeyRequest request;
+    AttestKeyRequest request(message_version_);
     request.SetKeyMaterial(*key_to_attest);
     request.attest_params.Reinitialize(*attest_params);
 
@@ -390,7 +391,7 @@
         return KM_ERROR_INVALID_INPUT_LENGTH;
     }
 
-    AttestKeyResponse response;
+    AttestKeyResponse response(message_version_);
     keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -438,11 +439,11 @@
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
     }
 
-    UpgradeKeyRequest request;
+    UpgradeKeyRequest request(message_version_);
     request.SetKeyMaterial(*key_to_upgrade);
     request.upgrade_params.Reinitialize(*upgrade_params);
 
-    UpgradeKeyResponse response;
+    UpgradeKeyResponse response(message_version_);
     keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -479,12 +480,12 @@
         *out_params = {};
     }
 
-    BeginOperationRequest request;
+    BeginOperationRequest request(message_version_);
     request.purpose = purpose;
     request.SetKeyMaterial(*key);
     request.additional_params.Reinitialize(*in_params);
 
-    BeginOperationResponse response;
+    BeginOperationResponse response(message_version_);
     keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -527,7 +528,7 @@
         *output = {};
     }
 
-    UpdateOperationRequest request;
+    UpdateOperationRequest request(message_version_);
     request.op_handle = operation_handle;
     if (in_params) {
         request.additional_params.Reinitialize(*in_params);
@@ -537,7 +538,7 @@
         request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
     }
 
-    UpdateOperationResponse response;
+    UpdateOperationResponse response(message_version_);
     keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -576,7 +577,9 @@
         return error_;
     }
     if (input && input->data_length > kMaximumFinishInputLength) {
-        return KM_ERROR_INVALID_ARGUMENT;
+        ALOGE("%zu-byte input to finish; only %zu bytes allowed",
+              input->data_length, kMaximumFinishInputLength);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
     }
 
     if (out_params) {
@@ -586,7 +589,7 @@
         *output = {};
     }
 
-    FinishOperationRequest request;
+    FinishOperationRequest request(message_version_);
     request.op_handle = operation_handle;
     if (signature && signature->data && signature->data_length > 0) {
         request.signature.Reinitialize(signature->data, signature->data_length);
@@ -598,7 +601,7 @@
         request.additional_params.Reinitialize(*in_params);
     }
 
-    FinishOperationResponse response;
+    FinishOperationResponse response(message_version_);
     keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -631,9 +634,9 @@
         return error_;
     }
 
-    AbortOperationRequest request;
+    AbortOperationRequest request(message_version_);
     request.op_handle = operation_handle;
-    AbortOperationResponse response;
+    AbortOperationResponse response(message_version_);
     return Send(KM_ABORT_OPERATION, request, &response);
 }
 
@@ -768,6 +771,9 @@
     ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
     int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
     if (rc < 0) {
+        // Reset the connection on tipc error
+        trusty_keymaster_disconnect();
+        trusty_keymaster_connect();
         ALOGE("tipc error: %d\n", rc);
         // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
         return translate_error(rc);
@@ -775,8 +781,7 @@
         ALOGV("Received %d byte response\n", rsp_size);
     }
 
-    const keymaster_message* msg = (keymaster_message*)recv_buf;
-    const uint8_t* p = msg->payload;
+    const uint8_t* p = recv_buf;
     if (!rsp->Deserialize(&p, p + rsp_size)) {
         ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
         return KM_ERROR_UNKNOWN_ERROR;
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
index 3bb5430..9227964 100644
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -15,8 +15,8 @@
  */
 #include <algorithm>
 #include <fstream>
+#include <memory>
 
-#include <UniquePtr.h>
 #include <gtest/gtest.h>
 #include <openssl/engine.h>
 
@@ -181,7 +181,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -200,7 +200,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8 - 1;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -217,7 +217,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8 + 1;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -272,7 +272,7 @@
 
     keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
     size_t message_len = 1024 * 7;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     // contents of message don't matter.
     uint8_t* signature;
     size_t siglen;
@@ -294,7 +294,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -315,7 +315,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -338,7 +338,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -360,7 +360,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -382,7 +382,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len + 1));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len + 1));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -422,7 +422,7 @@
 
     keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
     size_t message_len = 1024 * 7;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     // contents of message don't matter.
     uint8_t* signature;
     size_t siglen;
@@ -453,7 +453,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_size = 1024 /* key size */ / 8;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_size]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_size]);
     memset(message.get(), 'a', message_size);
     uint8_t* signature;
     size_t siglen;
@@ -491,9 +491,9 @@
 
 static void VerifySignature(const uint8_t* key, size_t key_len, const uint8_t* signature,
                             size_t signature_len, const uint8_t* message, size_t message_len) {
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
     ASSERT_TRUE(pkey.get() != NULL);
-    UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
     ASSERT_TRUE(ctx.get() != NULL);
     ASSERT_EQ(1, EVP_PKEY_verify_init(ctx.get()));
     if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
@@ -518,7 +518,7 @@
     // Sign a message so we can verify it with the exported pubkey.
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
index cdc2778..fbd0eb3 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -21,8 +21,11 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #include <log/log.h>
 #include <trusty/tipc.h>
 
@@ -31,7 +34,7 @@
 
 #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
 
-static int handle_ = 0;
+static int handle_ = -1;
 
 int trusty_keymaster_connect() {
     int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
@@ -45,7 +48,7 @@
 
 int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
                           uint32_t* out_size) {
-    if (handle_ == 0) {
+    if (handle_ < 0) {
         ALOGE("not connected\n");
         return -EINVAL;
     }
@@ -62,32 +65,43 @@
         ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
         return -errno;
     }
+    size_t out_max_size = *out_size;
+    *out_size = 0;
+    struct iovec iov[2];
+    struct keymaster_message header;
+    iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
+    while (true) {
+        iov[1] = {
+            .iov_base = out + *out_size,
+            .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, out_max_size - *out_size)};
+        rc = readv(handle_, iov, 2);
+        if (rc < 0) {
+            ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+                  strerror(errno));
+            return -errno;
+        }
 
-    rc = read(handle_, out, *out_size);
-    if (rc < 0) {
-        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
-              strerror(errno));
-        return -errno;
+        if ((size_t)rc < sizeof(struct keymaster_message)) {
+            ALOGE("invalid response size (%d)\n", (int)rc);
+            return -EINVAL;
+        }
+
+        if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
+            ALOGE("invalid command (%d)", header.cmd);
+            return -EINVAL;
+        }
+        *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+        if (header.cmd & KEYMASTER_STOP_BIT) {
+            break;
+        }
     }
 
-    if ((size_t)rc < sizeof(struct keymaster_message)) {
-        ALOGE("invalid response size (%d)\n", (int)rc);
-        return -EINVAL;
-    }
-
-    msg = (struct keymaster_message*)out;
-
-    if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
-        ALOGE("invalid command (%d)", msg->cmd);
-        return -EINVAL;
-    }
-
-    *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
     return rc;
 }
 
 void trusty_keymaster_disconnect() {
-    if (handle_ != 0) {
+    if (handle_ >= 0) {
         tipc_close(handle_);
     }
+    handle_ = -1;
 }
diff --git a/trusty/nvram/trusty_nvram_device.cpp b/trusty/nvram/trusty_nvram_device.cpp
index 2c50915..82a1228 100644
--- a/trusty/nvram/trusty_nvram_device.cpp
+++ b/trusty/nvram/trusty_nvram_device.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+
 #include <nvram/hal/nvram_device_adapter.h>
 
 #include "trusty_nvram_implementation.h"
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
index 3eff3f2..1e4fced 100644
--- a/trusty/storage/tests/Android.bp
+++ b/trusty/storage/tests/Android.bp
@@ -21,7 +21,6 @@
         "-g",
         "-Wall",
         "-Werror",
-        "-std=gnu++11",
         "-Wno-missing-field-initializers",
     ],
 
diff --git a/trusty/storage/tests/main.cpp b/trusty/storage/tests/main.cpp
index 1fd6f8d..4529136 100644
--- a/trusty/storage/tests/main.cpp
+++ b/trusty/storage/tests/main.cpp
@@ -16,7 +16,6 @@
 
 #include <assert.h>
 #include <stdint.h>
-#include <stdbool.h>
 #include <gtest/gtest.h>
 
 #include <trusty/lib/storage.h>
diff --git a/tzdatacheck/Android.bp b/tzdatacheck/Android.bp
deleted file mode 100644
index 00ad141..0000000
--- a/tzdatacheck/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-// ========================================================
-// Executable
-// ========================================================
-cc_binary {
-    name: "tzdatacheck",
-    host_supported: true,
-    srcs: ["tzdatacheck.cpp"],
-    shared_libs: [
-        "libbase",
-        "libcutils",
-        "liblog",
-    ],
-    cflags: ["-Werror"],
-}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
deleted file mode 100644
index 8fcd17f..0000000
--- a/tzdatacheck/tzdatacheck.cpp
+++ /dev/null
@@ -1,601 +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 <ctype.h>
-#include <errno.h>
-#include <ftw.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "android-base/logging.h"
-
-// The name of the directory that holds a staged time zone update distro. If this exists it should
-// replace the one in CURRENT_DIR_NAME.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* STAGED_DIR_NAME = "/staged";
-
-// The name of the directory that holds the (optional) installed time zone update distro.
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* CURRENT_DIR_NAME = "/current";
-
-// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
-// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
-static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
-
-// The name of the file containing the distro version information.
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char* DISTRO_VERSION_FILENAME = "/distro_version";
-
-// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
-// AAA.BBB is the major/minor version of the distro format (e.g. 001.001),
-// CCCCC is the rules version (e.g. 2016g)
-// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001)
-// We only need the first 13 to determine if it is suitable for the device.
-static const int DISTRO_VERSION_LENGTH = 13;
-
-// The major version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001";
-
-// The length of the distro format major version excluding the \0
-static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
-
-// The minor version of the distro format supported by this code as a null-terminated char[].
-// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
-static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
-
-// The length of the distro format minor version excluding the \0
-static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1;
-
-// The length of the distro format version. e.g. 001.001
-static const size_t SUPPORTED_DISTRO_VERSION_LEN =
-        SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1;
-
-// The length of the IANA rules version bytes. e.g. 2016a
-static const size_t RULES_VERSION_LEN = 5;
-
-// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
-static const size_t DISTRO_VERSION_RULES_IDX = 8;
-
-// See also libcore.tzdata.shared2.TimeZoneDistro.
-static const char* TZDATA_FILENAME = "/tzdata";
-
-// tzdata file header (as much as we need for the version):
-// byte[11] tzdata_version  -- e.g. "tzdata2012f"
-static const int TZ_HEADER_LENGTH = 11;
-
-static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
-static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
-
-static void usage() {
-    std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
-            "\n"
-            "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n"
-            "current Android release and better than or the same as base system timezone rules in\n"
-            "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
-            "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
-    exit(1);
-}
-
-/*
- * Opens a file and fills buffer with the first byteCount bytes from the file.
- * If the file does not exist or cannot be opened or is too short then false is returned.
- * If the bytes were read successfully then true is returned.
- */
-static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
-    FILE* file = fopen(fileName.c_str(), "r");
-    if (file == nullptr) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Error opening file " << fileName;
-        }
-        return false;
-    }
-    size_t bytesRead = fread(buffer, 1, byteCount, file);
-    fclose(file);
-    if (bytesRead != byteCount) {
-        LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
-        return false;
-    }
-    return true;
-}
-
-/*
- * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
- * otherwise.
- */
-static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
-    if (strncmp("tzdata", headerBytes, 6) != 0) {
-        LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
-        return false;
-    }
-    return true;
-}
-
-static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
-    for (size_t j = 0; j < count; j++) {
-      char toCheck = buffer[(*i)++];
-      if (!isdigit(toCheck)) {
-        return false;
-      }
-    }
-    return true;
-}
-
-static bool checkValidDistroVersion(const char* buffer) {
-    // See DISTRO_VERSION_LENGTH comments above for a description of the format.
-    size_t i = 0;
-    if (!checkDigits(buffer, 3, &i)) {
-      return false;
-    }
-    if (buffer[i++] != '.') {
-      return false;
-    }
-    if (!checkDigits(buffer, 3, &i)) {
-      return false;
-    }
-    if (buffer[i++] != '|') {
-      return false;
-    }
-    if (!checkDigits(buffer, 4, &i)) {
-      return false;
-    }
-    // Ignore the last character. It is assumed to be a letter but we don't check because it's not
-    // obvious what would happen at 'z'.
-    return true;
-}
-
-/* Return the parent directory of dirName. */
-static std::string getParentDir(const std::string& dirName) {
-    std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
-    return dirname(mutable_dirname.get());
-}
-
-/* Deletes a single file, symlink or directory. Called from nftw(). */
-static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
-    LOG(DEBUG) << "Inspecting " << fpath;
-    switch (typeflag) {
-    case FTW_F:
-    case FTW_SL:
-        LOG(DEBUG) << "Unlinking " << fpath;
-        if (unlink(fpath)) {
-            PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
-        }
-        break;
-    case FTW_D:
-    case FTW_DP:
-        LOG(DEBUG) << "Removing dir " << fpath;
-        if (rmdir(fpath)) {
-            PLOG(WARNING) << "Failed to remove dir " << fpath;
-        }
-        break;
-    default:
-        LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
-        break;
-    }
-    return 0;
-}
-
-enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
-
-static PathStatus checkPath(const std::string& path) {
-    struct stat buf;
-    if (stat(path.c_str(), &buf) != 0) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Unable to stat " << path;
-            return ERR;
-        }
-        return NONE;
-    }
-    return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
-}
-
-/*
- * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
- * cannot be accessed this method returns false.
- */
-static bool deleteFile(const std::string& fileToDelete) {
-    // Check whether the file exists.
-    PathStatus pathStatus = checkPath(fileToDelete);
-    if (pathStatus == NONE) {
-        LOG(INFO) << "Path " << fileToDelete << " does not exist";
-        return true;
-    }
-    if (pathStatus != IS_REG) {
-        LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
-        return false;
-    }
-
-    // Attempt the deletion.
-    int rc = unlink(fileToDelete.c_str());
-    if (rc != 0) {
-        PLOG(WARNING) << "unlink() failed for " << fileToDelete;
-    }
-    return rc == 0;
-}
-
-/*
- * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
- * of the way. If dirToDelete does not exist this function does nothing and returns true. If
- * dirToDelete is not a directory or cannot be accessed this method returns false.
- *
- * During deletion, this function first renames the directory to a temporary name. If the temporary
- * directory cannot be created, or the directory cannot be renamed, false is returned. After the
- * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
- * basis. Symlinks beneath the directory are not followed.
- */
-static bool deleteDir(const std::string& dirToDelete) {
-    // Check whether the dir exists.
-    int pathStatus = checkPath(dirToDelete);
-    if (pathStatus == NONE) {
-        LOG(INFO) << "Path " << dirToDelete << " does not exist";
-        return true;
-    }
-    if (pathStatus != IS_DIR) {
-        LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
-        return false;
-    }
-
-    // First, rename dirToDelete.
-
-    std::string tempDirNameTemplate = getParentDir(dirToDelete);
-    tempDirNameTemplate += "/tempXXXXXX";
-
-    // Create an empty directory with the temporary name. For this we need a non-const char*.
-    std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
-    strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
-    if (mkdtemp(&tempDirName[0]) == nullptr) {
-        PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
-        return false;
-    }
-
-    // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
-    int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
-    if (rc == -1) {
-        PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
-                << &tempDirName[0];
-        return false;
-    }
-
-    // Recursively delete contents of tempDirName.
-
-    rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
-            FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
-    if (rc == -1) {
-        LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
-    }
-    return true;
-}
-
-/*
- * Deletes the ConfigInstaller metadata directory.
- * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
- */
-static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
-    // Delete the update metadata
-    std::string dataUpdatesDirName(dataZoneInfoDir);
-    dataUpdatesDirName += "/updates";
-    LOG(INFO) << "Removing: " << dataUpdatesDirName;
-    if (!deleteDir(dataUpdatesDirName)) {
-        LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
-                << " was not successful";
-    }
-}
-
-/*
- * Deletes the timezone update distro directory.
- */
-static void deleteUpdateDistroDir(const std::string& distroDirName) {
-    LOG(INFO) << "Removing: " << distroDirName;
-    if (!deleteDir(distroDirName)) {
-        LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
-    }
-}
-
-static void handleStagedUninstall(const std::string& dataStagedDirName,
-                                  const std::string& dataCurrentDirName,
-                                  const PathStatus dataCurrentDirStatus) {
-    LOG(INFO) << "Staged operation is an uninstall.";
-
-    // Delete the current install directory.
-    switch (dataCurrentDirStatus) {
-        case NONE:
-            // This is unexpected: No uninstall should be staged if there is nothing to
-            // uninstall. Carry on anyway.
-            LOG(WARNING) << "No current install to delete.";
-            break;
-        case IS_DIR:
-            // This is normal. Delete the current install dir.
-            if (!deleteDir(dataCurrentDirName)) {
-                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                             << " was not successful";
-                // If this happens we don't know whether we were able to delete or not. We don't
-                // delete the staged operation so it will be retried next boot unless overridden.
-                return;
-            }
-            break;
-        case IS_REG:
-        default:
-            // This is unexpected: We can try to delete the unexpected file and carry on.
-            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                         << " is not actually a directory. Attempting deletion.";
-            if (!deleteFile(dataCurrentDirName)) {
-                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
-                return;
-            }
-            break;
-    }
-
-    // Delete the staged uninstall dir.
-    if (!deleteDir(dataStagedDirName)) {
-        LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                     << " was not successful";
-        // If this happens we don't know whether we were able to delete the staged operation
-        // or not.
-        return;
-    }
-    LOG(INFO) << "Staged uninstall complete.";
-}
-
-static void handleStagedInstall(const std::string& dataStagedDirName,
-                                const std::string& dataCurrentDirName,
-                                const PathStatus dataCurrentDirStatus) {
-    LOG(INFO) << "Staged operation is an install.";
-
-    switch (dataCurrentDirStatus) {
-        case NONE:
-            // This is expected: This is the first install.
-            LOG(INFO) << "No current install to replace.";
-            break;
-        case IS_DIR:
-            // This is expected: We are replacing an existing install.
-            // Delete the current dir so we can replace it.
-            if (!deleteDir(dataCurrentDirName)) {
-                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
-                             << " was not successful";
-                // If this happens, we cannot proceed.
-                return;
-            }
-            break;
-        case IS_REG:
-        default:
-            // This is unexpected: We can try to delete the unexpected file and carry on.
-            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                         << " is not actually a directory. Attempting deletion.";
-            if (!deleteFile(dataCurrentDirName)) {
-                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
-                return;
-            }
-            break;
-    }
-
-    // Move the staged dir so it is the new current dir, completing the install.
-    LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
-    int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
-    if (rc == -1) {
-        PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
-                      << &dataCurrentDirName[0];
-        return;
-    }
-
-    LOG(INFO) << "Staged install complete.";
-}
-/*
- * Process a staged operation if there is one.
- */
-static void processStagedOperation(const std::string& dataStagedDirName,
-                                   const std::string& dataCurrentDirName) {
-    PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
-
-    // Exit early for the common case.
-    if (dataStagedDirStatus == NONE) {
-        LOG(DEBUG) << "No staged time zone operation.";
-        return;
-    }
-
-    // Check known directory names are in a good starting state.
-    if (dataStagedDirStatus != IS_DIR) {
-        LOG(WARNING) << "Staged distro dir " << dataStagedDirName
-                     << " could not be accessed or is not a directory."
-                     << " stagedDirStatus=" << dataStagedDirStatus;
-        return;
-    }
-
-    // dataStagedDirStatus == IS_DIR.
-
-    // Work out whether there is anything currently installed.
-    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
-    if (dataCurrentDirStatus == ERR) {
-        LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
-                     << " dataCurrentDirStatus=" << dataCurrentDirStatus;
-        return;
-    }
-
-    // We must perform the staged operation.
-
-    // Check to see if the staged directory contains an uninstall or an install operation.
-    std::string uninstallTombStoneFile(dataStagedDirName);
-    uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
-    int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
-    if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
-        // Error case.
-        LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
-        return;
-    }
-    if (uninstallTombStoneFileStatus == IS_REG) {
-        handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
-    } else {
-        // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
-        handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
-    }
-}
-
-/*
- * After a platform update it is likely that timezone data found on the system partition will be
- * newer than the version found in the data partition. This tool detects this case and removes the
- * version in /data.
- *
- * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
- * paths for the metadata and current timezone data must match.
- *
- * Typically on device the two args will be:
- *   /system/usr/share/zoneinfo /data/misc/zoneinfo
- *
- * See usage() for usage notes.
- */
-int main(int argc, char* argv[]) {
-    if (argc != 3) {
-        usage();
-        return 1;
-    }
-
-    const char* systemZoneInfoDir = argv[1];
-    const char* dataZoneInfoDir = argv[2];
-
-    std::string dataStagedDirName(dataZoneInfoDir);
-    dataStagedDirName += STAGED_DIR_NAME;
-
-    std::string dataCurrentDirName(dataZoneInfoDir);
-    dataCurrentDirName += CURRENT_DIR_NAME;
-
-    // Check for an process any staged operation.
-    // If the staged operation could not be handled we still have to validate the current installed
-    // directory so we do not check for errors and do not quit early.
-    processStagedOperation(dataStagedDirName, dataCurrentDirName);
-
-    // Check the distro directory exists. If it does not, exit quickly: nothing to do.
-    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
-    if (dataCurrentDirStatus == NONE) {
-        LOG(INFO) << "timezone distro dir " << dataCurrentDirName
-                << " does not exist. No action required.";
-        return 0;
-    }
-
-    // If the distro directory path is not a directory or we can't stat() the path, exit with a
-    // warning: either there's a problem accessing storage or the world is not as it should be;
-    // nothing to do.
-    if (dataCurrentDirStatus != IS_DIR) {
-        LOG(WARNING) << "Current distro dir " << dataCurrentDirName
-                << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
-        return 2;
-    }
-
-    // Check the installed distro version.
-    std::string distroVersionFileName(dataCurrentDirName);
-    distroVersionFileName += DISTRO_VERSION_FILENAME;
-    std::vector<char> distroVersion;
-    distroVersion.reserve(DISTRO_VERSION_LENGTH);
-    bool distroVersionReadOk =
-            readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH);
-    if (!distroVersionReadOk) {
-        LOG(WARNING) << "distro version file " << distroVersionFileName
-                << " does not exist or is too short. Deleting distro dir.";
-        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 3;
-    }
-
-    if (!checkValidDistroVersion(distroVersion.data())) {
-        LOG(WARNING) << "distro version file " << distroVersionFileName
-                << " is not valid. Deleting distro dir.";
-        // Implies the contents of the data partition is corrupt in some way. Try to clean up.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 4;
-    }
-
-    std::string actualDistroVersion =
-            std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN);
-    // Check the first 3 bytes of the distro version: these are the major version (e.g. 001).
-    // It must match the one we support exactly to be ok.
-    if (strncmp(
-            &distroVersion[0],
-            SUPPORTED_DISTRO_MAJOR_VERSION,
-            SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) {
-
-        LOG(INFO) << "distro version file " << distroVersionFileName
-                << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION
-                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
-        // This implies there has been an OTA and the installed distro is not compatible with the
-        // new version of Android. Remove the installed distro.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 5;
-    }
-
-    // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001).
-    // If the version in the distro is < the minor version required by this device it cannot be
-    // used.
-    if (strncmp(
-            &distroVersion[4],
-            SUPPORTED_DISTRO_MINOR_VERSION,
-            SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) {
-
-        LOG(INFO) << "distro version file " << distroVersionFileName
-                << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION
-                << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
-        // This implies there has been an OTA and the installed distro is not compatible with the
-        // new version of Android. Remove the installed distro.
-        deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-        deleteUpdateDistroDir(dataCurrentDirName);
-        return 5;
-    }
-
-    // Read the system rules version out of the /system tzdata file.
-    std::string systemTzDataFileName(systemZoneInfoDir);
-    systemTzDataFileName += TZDATA_FILENAME;
-    std::vector<char> systemTzDataHeader;
-    systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
-    bool systemFileExists =
-            readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
-    if (!systemFileExists) {
-        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
-        LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
-        return 6;
-    }
-    if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
-        // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
-        LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
-        return 7;
-    }
-
-    // Compare the distro rules version against the system rules version.
-    if (strncmp(
-            &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
-            &distroVersion[DISTRO_VERSION_RULES_IDX],
-            RULES_VERSION_LEN) <= 0) {
-        LOG(INFO) << "Found an installed distro but it is valid. No action taken.";
-        // Implies there is an installed update, but it is good.
-        return 0;
-    }
-
-    // Implies there has been an OTA and the system version of the timezone rules is now newer
-    // than the version installed in /data. Remove the installed distro.
-    LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in "
-            << systemTzDataFileName << "; fixing...";
-
-    deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
-    deleteUpdateDistroDir(dataCurrentDirName);
-    return 0;
-}