Merge "Remove CHECK in AdjustEncodedValue."
diff --git a/adb/Android.bp b/adb/Android.bp
index 6234af1..36bfad4 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -355,6 +355,7 @@
     compile_multilib: "both",
 
     srcs: [
+        "daemon/abb_service.cpp",
         "daemon/file_sync_service.cpp",
         "daemon/framebuffer_service.cpp",
         "daemon/mdns.cpp",
@@ -391,6 +392,14 @@
         "libmdnssd",
         "libselinux",
     ],
+
+    target: {
+        recovery: {
+            exclude_srcs: [
+                "daemon/abb_service.cpp",
+            ],
+        },
+    },
 }
 
 cc_library {
@@ -455,6 +464,40 @@
     ],
 }
 
+cc_binary {
+    name: "abb",
+
+    defaults: ["adb_defaults"],
+    recovery_available: false,
+
+    srcs: [
+        "daemon/abb.cpp",
+    ],
+
+    cflags: [
+        "-D_GNU_SOURCE",
+        "-Wno-deprecated-declarations",
+    ],
+
+    strip: {
+        keep_symbols: true,
+    },
+
+    static_libs: [
+        "libadbd_core",
+        "libadbd_services",
+        "libcmd",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libutils",
+        "libselinux",
+    ],
+}
+
 cc_test {
     name: "adbd_test",
     defaults: ["adb_defaults"],
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 9c0eeca..06e4c50 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -357,9 +357,14 @@
 
     case A_OPEN: /* OPEN(local-id, 0, "destination") */
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
-            // TODO: Switch to string_view.
-            std::string address(p->payload.begin(), p->payload.end());
-            asocket* s = create_local_service_socket(address.c_str(), t);
+            std::string_view address(p->payload.begin(), p->payload.size());
+
+            // Historically, we received service names as a char*, and stopped at the first NUL
+            // byte. The client sent strings with null termination, which post-string_view, start
+            // being interpreted as part of the string, unless we explicitly strip them.
+            address = StripTrailingNulls(address);
+
+            asocket* s = create_local_service_socket(address, t);
             if (s == nullptr) {
                 send_close(0, p->msg.arg0, t);
             } else {
@@ -600,7 +605,7 @@
     fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
     fprintf(stderr, "Server had pid: %d\n", pid);
 
-    android::base::unique_fd fd(unix_open(GetLogFilePath().c_str(), O_RDONLY));
+    android::base::unique_fd fd(unix_open(GetLogFilePath(), O_RDONLY));
     if (fd == -1) return;
 
     // Let's not show more than 128KiB of log...
diff --git a/adb/adb.h b/adb/adb.h
index cdd6346..d79cd2d 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -139,9 +139,9 @@
 atransport* find_emulator_transport_by_console_port(int console_port);
 #endif
 
-int service_to_fd(const char* name, atransport* transport);
+int service_to_fd(std::string_view name, atransport* transport);
 #if !ADB_HOST
-unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
 #endif
 
 #if ADB_HOST
@@ -153,6 +153,10 @@
 #endif
 
 #if !ADB_HOST
+unique_fd execute_binder_command(std::string_view command);
+#endif
+
+#if !ADB_HOST
 int init_jdwp(void);
 asocket* create_jdwp_service_socket();
 asocket* create_jdwp_tracker_service_socket();
@@ -202,6 +206,9 @@
 
 #define CHUNK_SIZE (64 * 1024)
 
+// Argument delimeter for adb abb command.
+#define ABB_ARG_DELIMETER ('\0')
+
 #if !ADB_HOST
 #define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
 #define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 6cc274b..605d27d 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -20,6 +20,11 @@
 
 #include <unistd.h>
 
+#if !ADB_HOST
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
 #include <thread>
 
 #include <android-base/stringprintf.h>
@@ -38,7 +43,8 @@
 
     // The cost of sending two strings outweighs the cost of formatting.
     // "adb sync" performance is affected by this.
-    return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+    auto str = android::base::StringPrintf("%04x", length).append(s);
+    return WriteFdExactly(fd, str);
 }
 
 bool ReadProtocolString(int fd, std::string* s, std::string* error) {
@@ -181,3 +187,79 @@
         return false;
     }
 }
+
+#if defined(__linux__)
+bool SendFileDescriptor(int socket_fd, int fd) {
+    struct msghdr msg;
+    struct iovec iov;
+    char dummy = '!';
+    union {
+        cmsghdr cm;
+        char buffer[CMSG_SPACE(sizeof(int))];
+    } cm_un;
+
+    iov.iov_base = &dummy;
+    iov.iov_len = 1;
+    msg.msg_name = nullptr;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_flags = 0;
+    msg.msg_control = cm_un.buffer;
+    msg.msg_controllen = sizeof(cm_un.buffer);
+
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    ((int*)CMSG_DATA(cmsg))[0] = fd;
+
+    int ret = TEMP_FAILURE_RETRY(sendmsg(socket_fd, &msg, 0));
+    if (ret < 0) {
+        D("sending file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
+        return false;
+    }
+
+    D("sent file descriptor %d to via socket %d", fd, socket_fd);
+    return true;
+}
+
+bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error) {
+    char dummy = '!';
+    union {
+        cmsghdr cm;
+        char buffer[CMSG_SPACE(sizeof(int))];
+    } cm_un;
+
+    iovec iov;
+    iov.iov_base = &dummy;
+    iov.iov_len = 1;
+
+    msghdr msg;
+    msg.msg_name = nullptr;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_flags = 0;
+    msg.msg_control = cm_un.buffer;
+    msg.msg_controllen = sizeof(cm_un.buffer);
+
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    ((int*)(CMSG_DATA(cmsg)))[0] = -1;
+
+    int rc = TEMP_FAILURE_RETRY(recvmsg(socket_fd, &msg, 0));
+    if (rc <= 0) {
+        *error = perror_str("receiving file descriptor via socket failed");
+        D("receiving file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
+        return false;
+    }
+
+    fd->reset(((int*)(CMSG_DATA(cmsg)))[0]);
+    D("received file descriptor %d to via socket %d", fd->get(), socket_fd);
+
+    return true;
+}
+#endif
diff --git a/adb/adb_io.h b/adb/adb_io.h
index aa550af..2ccaa32 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -21,6 +21,8 @@
 
 #include <string>
 
+#include "adb_unique_fd.h"
+
 // Sends the protocol "OKAY" message.
 bool SendOkay(int fd);
 
@@ -73,4 +75,12 @@
 // Same as above, but formats the string to send.
 bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
 
+#if !ADB_HOST
+// Sends an FD via Unix domain socket.
+bool SendFileDescriptor(int socket_fd, int fd);
+
+// Receives an FD via Unix domain socket.
+bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error);
+#endif
+
 #endif /* ADB_IO_H */
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 051ab73..be457a6 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -105,7 +105,7 @@
         s = create_local_socket(fd);
         if (s) {
             s->transport = listener->transport;
-            connect_to_remote(s, listener->connect_to.c_str());
+            connect_to_remote(s, listener->connect_to);
             return;
         }
 
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index a024a89..2bd6a3e 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -72,8 +72,7 @@
 }
 
 void start_device_log(void) {
-    int fd = unix_open(get_log_file_name().c_str(),
-                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+    int fd = unix_open(get_log_file_name(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
     if (fd == -1) {
         return;
     }
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 6d12225..8253487 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
 #include <condition_variable>
 #include <mutex>
 #include <string>
+#include <string_view>
+#include <type_traits>
 #include <vector>
 
 #include <android-base/macros.h>
@@ -94,3 +96,47 @@
 };
 
 std::string GetLogFilePath();
+
+inline std::string_view StripTrailingNulls(std::string_view str) {
+    size_t n = 0;
+    for (auto it = str.rbegin(); it != str.rend(); ++it) {
+        if (*it != '\0') {
+            break;
+        }
+        ++n;
+    }
+
+    str.remove_suffix(n);
+    return str;
+}
+
+// Base-10 stroll on a string_view.
+template <typename T>
+inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining) {
+    if (str.empty() || !isdigit(str[0])) {
+        return false;
+    }
+
+    T value = 0;
+    std::string_view::iterator it;
+    constexpr T max = std::numeric_limits<T>::max();
+    for (it = str.begin(); it != str.end() && isdigit(*it); ++it) {
+        if (value > max / 10) {
+            return false;
+        }
+
+        value *= 10;
+
+        T digit = *it - '0';
+        if (value > max - digit) {
+            return false;
+        }
+
+        value += digit;
+    }
+    *result = value;
+    if (remaining) {
+        *remaining = str.substr(it - str.begin());
+    }
+    return true;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 870f6f0..bb09425 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -181,3 +181,48 @@
     EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error));
     EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error));
 }
+
+void TestParseUint(std::string_view string, bool expected_success, uint32_t expected_value = 0) {
+    // Standalone.
+    {
+        uint32_t value;
+        std::string_view remaining;
+        bool success = ParseUint(&value, string, &remaining);
+        EXPECT_EQ(success, expected_success);
+        if (expected_success) {
+            EXPECT_EQ(value, expected_value);
+        }
+        EXPECT_TRUE(remaining.empty());
+    }
+
+    // With trailing text.
+    {
+        std::string text = std::string(string) + "foo";
+        uint32_t value;
+        std::string_view remaining;
+        bool success = ParseUint(&value, text, &remaining);
+        EXPECT_EQ(success, expected_success);
+        if (expected_success) {
+            EXPECT_EQ(value, expected_value);
+            EXPECT_EQ(remaining, "foo");
+        }
+    }
+}
+
+TEST(adb_utils, ParseUint) {
+    TestParseUint("", false);
+    TestParseUint("foo", false);
+    TestParseUint("foo123", false);
+    TestParseUint("-1", false);
+
+    TestParseUint("123", true, 123);
+    TestParseUint("9999999999999999999999999", false);
+    TestParseUint(std::to_string(UINT32_MAX), true, UINT32_MAX);
+    TestParseUint("0" + std::to_string(UINT32_MAX), true, UINT32_MAX);
+    TestParseUint(std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+    TestParseUint("0" + std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+
+    std::string x = std::to_string(UINT32_MAX) + "123";
+    std::string_view substr = std::string_view(x).substr(0, std::to_string(UINT32_MAX).size());
+    TestParseUint(substr, true, UINT32_MAX);
+}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 8676214..ce51b1c 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -579,12 +579,8 @@
 //
 // On success returns the remote exit code if |use_shell_protocol| is true,
 // 0 otherwise. On failure returns 1.
-static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
-                       char escape_char,
-                       const std::string& command) {
-    std::string service_string = ShellServiceString(use_shell_protocol,
-                                                    type_arg, command);
-
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg, char escape_char,
+                       bool empty_command, const std::string& service_string) {
     // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
     // Use |use_shell_protocol| to determine whether to allow a command longer than that.
     if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
@@ -595,8 +591,7 @@
     // Make local stdin raw if the device allocates a PTY, which happens if:
     //   1. We are explicitly asking for a PTY shell, or
     //   2. We don't specify shell type and are starting an interactive session.
-    bool raw_stdin = (type_arg == kShellServiceArgPty ||
-                      (type_arg.empty() && command.empty()));
+    bool raw_stdin = (type_arg == kShellServiceArgPty || (type_arg.empty() && empty_command));
 
     std::string error;
     int fd = adb_connect(service_string, &error);
@@ -756,7 +751,29 @@
         command = android::base::Join(std::vector<const char*>(argv + optind, argv + argc), ' ');
     }
 
-    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
+    std::string service_string = ShellServiceString(use_shell_protocol, shell_type_arg, command);
+    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command.empty(),
+                       service_string);
+}
+
+static int adb_abb(int argc, const char** argv) {
+    // Defaults.
+    constexpr char escape_char = '~';  // -e
+    constexpr bool use_shell_protocol = true;
+    constexpr auto shell_type_arg = kShellServiceArgRaw;
+    constexpr bool empty_command = false;
+
+    std::string service_string("abb:");
+    for (auto i = optind; i < argc; ++i) {
+        service_string.append(argv[i]);
+        service_string.push_back(ABB_ARG_DELIMETER);
+    }
+
+    D("abb -e 0x%x [%*.s]\n", escape_char, static_cast<int>(service_string.size()),
+      service_string.data());
+
+    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, empty_command,
+                       service_string);
 }
 
 static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
@@ -1546,14 +1563,13 @@
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
         return adb_query_command(query);
-    }
-    else if (!strcmp(argv[0], "emu")) {
+    } else if (!strcmp(argv[0], "abb")) {
+        return adb_abb(argc, argv);
+    } else if (!strcmp(argv[0], "emu")) {
         return adb_send_emulator_command(argc, argv, serial);
-    }
-    else if (!strcmp(argv[0], "shell")) {
+    } else if (!strcmp(argv[0], "shell")) {
         return adb_shell(argc, argv);
-    }
-    else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
+    } else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
 
         if (argc < 2) error_exit("usage: adb %s command", argv[0]);
@@ -1581,11 +1597,9 @@
 
         adb_close(fd);
         return 0;
-    }
-    else if (!strcmp(argv[0], "kill-server")) {
+    } else if (!strcmp(argv[0], "kill-server")) {
         return adb_kill_server() ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "sideload")) {
+    } else if (!strcmp(argv[0], "sideload")) {
         if (argc != 2) error_exit("sideload requires an argument");
         if (adb_sideload_host(argv[1])) {
             return 1;
@@ -1695,8 +1709,7 @@
     else if (!strcmp(argv[0], "ls")) {
         if (argc != 2) error_exit("ls requires an argument");
         return do_sync_ls(argv[1]) ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "push")) {
+    } else if (!strcmp(argv[0], "push")) {
         bool copy_attrs = false;
         bool sync = false;
         std::vector<const char*> srcs;
@@ -1705,8 +1718,7 @@
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
         if (srcs.empty() || !dst) error_exit("push requires an argument");
         return do_sync_push(srcs, dst, sync) ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "pull")) {
+    } else if (!strcmp(argv[0], "pull")) {
         bool copy_attrs = false;
         std::vector<const char*> srcs;
         const char* dst = ".";
@@ -1714,20 +1726,16 @@
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
         if (srcs.empty()) error_exit("pull requires an argument");
         return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "install")) {
+    } else if (!strcmp(argv[0], "install")) {
         if (argc < 2) error_exit("install requires an argument");
         return install_app(argc, argv);
-    }
-    else if (!strcmp(argv[0], "install-multiple")) {
+    } else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) error_exit("install-multiple requires an argument");
         return install_multiple_app(argc, argv);
-    }
-    else if (!strcmp(argv[0], "uninstall")) {
+    } else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) error_exit("uninstall requires an argument");
         return uninstall_app(argc, argv);
-    }
-    else if (!strcmp(argv[0], "sync")) {
+    } else if (!strcmp(argv[0], "sync")) {
         std::string src;
         bool list_only = false;
         if (argc < 2) {
@@ -1757,34 +1765,28 @@
         return 0;
     }
     /* passthrough commands */
-    else if (!strcmp(argv[0],"get-state") ||
-        !strcmp(argv[0],"get-serialno") ||
-        !strcmp(argv[0],"get-devpath"))
-    {
+    else if (!strcmp(argv[0], "get-state") || !strcmp(argv[0], "get-serialno") ||
+             !strcmp(argv[0], "get-devpath")) {
         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")) {
+    else if (!strcmp(argv[0], "logcat") || !strcmp(argv[0], "lolcat") ||
+             !strcmp(argv[0], "longcat")) {
         return logcat(argc, argv);
-    }
-    else if (!strcmp(argv[0],"ppp")) {
+    } else if (!strcmp(argv[0], "ppp")) {
         return ppp(argc, argv);
-    }
-    else if (!strcmp(argv[0], "start-server")) {
+    } else if (!strcmp(argv[0], "start-server")) {
         std::string error;
         const int result = adb_connect("host:start-server", &error);
         if (result < 0) {
             fprintf(stderr, "error: %s\n", error.c_str());
         }
         return result;
-    }
-    else if (!strcmp(argv[0], "backup")) {
+    } else if (!strcmp(argv[0], "backup")) {
         return backup(argc, argv);
-    }
-    else if (!strcmp(argv[0], "restore")) {
+    } else if (!strcmp(argv[0], "restore")) {
         return restore(argc, argv);
-    }
-    else if (!strcmp(argv[0], "keygen")) {
+    } else if (!strcmp(argv[0], "keygen")) {
         if (argc != 2) error_exit("keygen requires an argument");
         // Always print key generation information for keygen command.
         adb_trace_enable(AUTH);
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index fb581a6..2ee81a9 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -40,7 +40,7 @@
 
 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);
+    int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
     if (fd == -1) {
         PLOG(FATAL) << "cannot open " << log_file_path;
     }
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index f1bf559..1168958 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -39,6 +39,7 @@
 #include <list>
 #include <mutex>
 #include <string>
+#include <string_view>
 #include <thread>
 
 #include <android-base/file.h>
@@ -90,7 +91,7 @@
 static auto& g_usb_handles_mutex = *new std::mutex();
 static auto& g_usb_handles = *new std::list<usb_handle*>();
 
-static int is_known_device(const char* dev_name) {
+static int is_known_device(std::string_view dev_name) {
     std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
     for (usb_handle* usb : g_usb_handles) {
         if (usb->path == dev_name) {
@@ -152,11 +153,11 @@
             if (contains_non_digit(de->d_name)) continue;
 
             std::string dev_name = bus_name + "/" + de->d_name;
-            if (is_known_device(dev_name.c_str())) {
+            if (is_known_device(dev_name)) {
                 continue;
             }
 
-            int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+            int fd = unix_open(dev_name, O_RDONLY | O_CLOEXEC);
             if (fd == -1) {
                 continue;
             }
@@ -535,10 +536,10 @@
     // Initialize mark so we don't get garbage collected after the device scan.
     usb->mark = true;
 
-    usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+    usb->fd = unix_open(usb->path, O_RDWR | O_CLOEXEC);
     if (usb->fd == -1) {
         // Opening RW failed, so see if we have RO access.
-        usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+        usb->fd = unix_open(usb->path, O_RDONLY | O_CLOEXEC);
         if (usb->fd == -1) {
             D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
             return;
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
new file mode 100644
index 0000000..f69babe
--- /dev/null
+++ b/adb/daemon/abb.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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 "adb.h"
+#include "adb_io.h"
+#include "shell_service.h"
+
+#include "cmd.h"
+
+#include <sys/wait.h>
+
+namespace {
+
+class AdbFdTextOutput : public android::TextOutput {
+  public:
+    explicit AdbFdTextOutput(int fd) : mFD(fd) {}
+
+  private:
+    android::status_t print(const char* txt, size_t len) override {
+        return WriteFdExactly(mFD, txt, len) ? android::OK : -errno;
+    }
+    void moveIndent(int delta) override { /*not implemented*/
+    }
+
+    void pushBundle() override { /*not implemented*/
+    }
+    void popBundle() override { /*not implemented*/
+    }
+
+  private:
+    int mFD;
+};
+
+std::vector<std::string_view> parseCmdArgs(std::string_view args) {
+    std::vector<std::string_view> argv;
+
+    char delim = ABB_ARG_DELIMETER;
+    size_t size = args.size();
+    size_t base = 0;
+    while (base < size) {
+        size_t found;
+        for (found = base; found < size && args[found] && args[found] != delim; ++found)
+            ;
+        if (found > base) {
+            argv.emplace_back(args.substr(base, found - base));
+        }
+        base = found + 1;
+    }
+
+    return argv;
+}
+
+}  // namespace
+
+static int execCmd(std::string_view args, int in, int out, int err) {
+    AdbFdTextOutput oin(out);
+    AdbFdTextOutput oerr(err);
+    return cmdMain(parseCmdArgs(args), oin, oerr, in, out, err, RunMode::kLibrary);
+}
+
+int main(int argc, char* const argv[]) {
+    signal(SIGPIPE, SIG_IGN);
+
+    int fd = STDIN_FILENO;
+    std::string data;
+    while (true) {
+        std::string error;
+        if (!ReadProtocolString(fd, &data, &error)) {
+            PLOG(ERROR) << "Failed to read message: " << error;
+            break;
+        }
+
+        auto result = StartCommandInProcess(std::move(data), &execCmd);
+        if (!SendFileDescriptor(fd, result)) {
+            PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
+            break;
+        }
+    }
+}
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
new file mode 100644
index 0000000..817aea1
--- /dev/null
+++ b/adb/daemon/abb_service.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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 "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "shell_service.h"
+
+namespace {
+
+struct AbbProcess;
+static auto& abbp = *new std::unique_ptr<AbbProcess>(std::make_unique<AbbProcess>());
+
+struct AbbProcess {
+    unique_fd sendCommand(std::string_view command);
+
+  private:
+    static unique_fd startAbbProcess(unique_fd* error_fd);
+
+    static constexpr auto kRetries = 2;
+    static constexpr auto kErrorProtocol = SubprocessProtocol::kShell;
+
+    std::mutex locker_;
+    unique_fd socket_fd_;
+};
+
+unique_fd AbbProcess::sendCommand(std::string_view command) {
+    std::unique_lock lock{locker_};
+
+    for (int i = 0; i < kRetries; ++i) {
+        unique_fd error_fd;
+        if (socket_fd_ == -1) {
+            socket_fd_ = startAbbProcess(&error_fd);
+        }
+        if (socket_fd_ == -1) {
+            LOG(ERROR) << "failed to start abb process";
+            return error_fd;
+        }
+
+        if (!SendProtocolString(socket_fd_, std::string(command))) {
+            PLOG(ERROR) << "failed to send command to abb";
+            socket_fd_.reset();
+            continue;
+        }
+
+        unique_fd fd;
+        std::string error;
+        if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
+            LOG(ERROR) << "failed to receive FD from abb: " << error;
+            socket_fd_.reset();
+            continue;
+        }
+
+        return fd;
+    }
+
+    LOG(ERROR) << "abb is unavailable";
+    socket_fd_.reset();
+    return ReportError(kErrorProtocol, "abb is unavailable");
+}
+
+unique_fd AbbProcess::startAbbProcess(unique_fd* error_fd) {
+    constexpr auto abb_process_type = SubprocessType::kRaw;
+    constexpr auto abb_protocol = SubprocessProtocol::kNone;
+    constexpr auto make_pty_raw = false;
+    return StartSubprocess("abb", "dumb", abb_process_type, abb_protocol, make_pty_raw,
+                           kErrorProtocol, error_fd);
+}
+
+}  // namespace
+
+unique_fd execute_binder_command(std::string_view command) {
+    return abbp->sendCommand(command);
+}
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 1363976..f02cc13 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -237,30 +237,7 @@
         CHECK(!proc->out_fds.empty());
 
         int fd = proc->out_fds.back().get();
-        struct cmsghdr* cmsg;
-        struct msghdr msg;
-        struct iovec iov;
-        char dummy = '!';
-        char buffer[sizeof(struct cmsghdr) + sizeof(int)];
-
-        iov.iov_base = &dummy;
-        iov.iov_len = 1;
-        msg.msg_name = nullptr;
-        msg.msg_namelen = 0;
-        msg.msg_iov = &iov;
-        msg.msg_iovlen = 1;
-        msg.msg_flags = 0;
-        msg.msg_control = buffer;
-        msg.msg_controllen = sizeof(buffer);
-
-        cmsg = CMSG_FIRSTHDR(&msg);
-        cmsg->cmsg_len = msg.msg_controllen;
-        cmsg->cmsg_level = SOL_SOCKET;
-        cmsg->cmsg_type = SCM_RIGHTS;
-        ((int*)CMSG_DATA(cmsg))[0] = fd;
-
-        int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
-        if (ret < 0) {
+        if (!SendFileDescriptor(socket, fd)) {
             D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
             goto CloseProcess;
         }
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 1a92317..80b3e06 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -103,7 +103,7 @@
 
 bool make_block_device_writable(const std::string& dev) {
     if (dev_is_overlayfs(dev)) return true;
-    int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
+    int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         return false;
     }
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 3182ddd..5ae210f 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -151,14 +151,17 @@
     kick_transport(t);
 }
 
-unique_fd reverse_service(const char* command, atransport* transport) {
+unique_fd reverse_service(std::string_view command, atransport* transport) {
+    // TODO: Switch handle_forward_request to std::string_view.
+    std::string str(command);
+
     int s[2];
     if (adb_socketpair(s)) {
         PLOG(ERROR) << "cannot create service socket pair.";
         return unique_fd{};
     }
     VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
-    if (!handle_forward_request(command, transport, s[1])) {
+    if (!handle_forward_request(str.c_str(), transport, s[1])) {
         SendFail(s[1], "not a reverse forwarding command");
     }
     adb_close(s[1]);
@@ -167,15 +170,16 @@
 
 // Shell service string can look like:
 //   shell[,arg1,arg2,...]:[command]
-unique_fd ShellService(const std::string& args, const atransport* transport) {
+unique_fd ShellService(std::string_view args, const atransport* transport) {
     size_t delimiter_index = args.find(':');
     if (delimiter_index == std::string::npos) {
         LOG(ERROR) << "No ':' found in shell service arguments: " << args;
         return unique_fd{};
     }
 
-    const std::string service_args = args.substr(0, delimiter_index);
-    const std::string command = args.substr(delimiter_index + 1);
+    // TODO: android::base::Split(const std::string_view&, ...)
+    std::string service_args(args.substr(0, delimiter_index));
+    std::string command(args.substr(delimiter_index + 1));
 
     // Defaults:
     //   PTY for interactive, raw for non-interactive.
@@ -192,15 +196,15 @@
             type = SubprocessType::kPty;
         } else if (arg == kShellServiceArgShellProtocol) {
             protocol = SubprocessProtocol::kShell;
-        } else if (android::base::StartsWith(arg, "TERM=")) {
-            terminal_type = arg.substr(5);
+        } else if (arg.starts_with("TERM=")) {
+            terminal_type = arg.substr(strlen("TERM="));
         } else if (!arg.empty()) {
             // This is not an error to allow for future expansion.
             LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
         }
     }
 
-    return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+    return StartSubprocess(command, terminal_type.c_str(), type, protocol);
 }
 
 static void spin_service(unique_fd fd) {
@@ -323,59 +327,77 @@
     return nullptr;
 }
 
-unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
-    if (!strncmp("dev:", name, 4)) {
-        return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
-    } else if (!strncmp(name, "framebuffer:", 12)) {
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+#ifndef __ANDROID_RECOVERY__
+    if (name.starts_with("abb:")) {
+        name.remove_prefix(strlen("abb:"));
+        return execute_binder_command(name);
+    }
+#endif
+
+    if (name.starts_with("dev:")) {
+        name.remove_prefix(strlen("dev:"));
+        return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+    } else if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (!strncmp(name, "jdwp:", 5)) {
-        return create_jdwp_connection_fd(atoi(name + 5));
-    } else if (!strncmp(name, "shell", 5)) {
-        return ShellService(name + 5, transport);
-    } else if (!strncmp(name, "exec:", 5)) {
-        return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "sync:", 5)) {
+    } else if (name.starts_with("jdwp:")) {
+        name.remove_prefix(strlen("jdwp:"));
+        std::string str(name);
+        return create_jdwp_connection_fd(atoi(str.c_str()));
+    } else if (name.starts_with("shell")) {
+        name.remove_prefix(strlen("shell"));
+        return ShellService(name, transport);
+    } else if (name.starts_with("exec:")) {
+        name.remove_prefix(strlen("exec:"));
+        return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
+    } else if (name.starts_with("sync:")) {
         return create_service_thread("sync", file_sync_service);
-    } else if (!strncmp(name, "remount:", 8)) {
-        std::string options(name + strlen("remount:"));
+    } else if (name.starts_with("remount:")) {
+        std::string arg(name.begin() + strlen("remount:"), name.end());
         return create_service_thread("remount",
-                                     std::bind(remount_service, std::placeholders::_1, options));
-    } else if (!strncmp(name, "reboot:", 7)) {
-        std::string arg(name + strlen("reboot:"));
+                                     std::bind(remount_service, std::placeholders::_1, arg));
+    } else if (name.starts_with("reboot:")) {
+        std::string arg(name.begin() + strlen("reboot:"), name.end());
         return create_service_thread("reboot",
                                      std::bind(reboot_service, std::placeholders::_1, arg));
-    } else if (!strncmp(name, "root:", 5)) {
+    } else if (name.starts_with("root:")) {
         return create_service_thread("root", restart_root_service);
-    } else if (!strncmp(name, "unroot:", 7)) {
+    } else if (name.starts_with("unroot:")) {
         return create_service_thread("unroot", restart_unroot_service);
-    } else if (!strncmp(name, "backup:", 7)) {
-        return StartSubprocess(
-                android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
-                nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "restore:", 8)) {
+    } else if (name.starts_with("backup:")) {
+        name.remove_prefix(strlen("backup:"));
+        std::string cmd = "/system/bin/bu backup ";
+        cmd += name;
+        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (name.starts_with("restore:")) {
         return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "tcpip:", 6)) {
+    } else if (name.starts_with("tcpip:")) {
+        name.remove_prefix(strlen("tcpip:"));
+        std::string str(name);
+
         int port;
-        if (sscanf(name + 6, "%d", &port) != 1) {
+        if (sscanf(str.c_str(), "%d", &port) != 1) {
             return unique_fd{};
         }
         return create_service_thread("tcp",
                                      std::bind(restart_tcp_service, std::placeholders::_1, port));
-    } else if (!strncmp(name, "usb:", 4)) {
+    } else if (name.starts_with("usb:")) {
         return create_service_thread("usb", restart_usb_service);
-    } else if (!strncmp(name, "reverse:", 8)) {
-        return reverse_service(name + 8, transport);
-    } else if (!strncmp(name, "disable-verity:", 15)) {
+    } else if (name.starts_with("reverse:")) {
+        name.remove_prefix(strlen("reverse:"));
+        return reverse_service(name, transport);
+    } else if (name.starts_with("disable-verity:")) {
         return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
                                                             std::placeholders::_1, false));
-    } else if (!strncmp(name, "enable-verity:", 15)) {
+    } else if (name.starts_with("enable-verity:")) {
         return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
                                                              std::placeholders::_1, true));
-    } else if (!strcmp(name, "reconnect")) {
+    } else if (name == "reconnect") {
         return create_service_thread(
                 "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
-    } else if (!strcmp(name, "spin")) {
+    } else if (name == "spin") {
         return create_service_thread("spin", spin_service);
     }
 
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 8805fc1..455595f 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -140,8 +140,8 @@
 
 class Subprocess {
   public:
-    Subprocess(const std::string& command, const char* terminal_type,
-               SubprocessType type, SubprocessProtocol protocol);
+    Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+               SubprocessProtocol protocol, bool make_pty_raw);
     ~Subprocess();
 
     const std::string& command() const { return command_; }
@@ -154,6 +154,10 @@
     // and exec's the child. Returns false and sets error on failure.
     bool ForkAndExec(std::string* _Nonnull error);
 
+    // Sets up FDs, starts a thread executing command and the manager thread,
+    // Returns false and sets error on failure.
+    bool ExecInProcess(Command command, std::string* _Nonnull error);
+
     // Start the subprocess manager thread. Consumes the subprocess, regardless of success.
     // Returns false and sets error on failure.
     static bool StartThread(std::unique_ptr<Subprocess> subprocess,
@@ -177,9 +181,9 @@
 
     const std::string command_;
     const std::string terminal_type_;
-    bool make_pty_raw_ = false;
     SubprocessType type_;
     SubprocessProtocol protocol_;
+    bool make_pty_raw_;
     pid_t pid_ = -1;
     unique_fd local_socket_sfd_;
 
@@ -191,25 +195,13 @@
     DISALLOW_COPY_AND_ASSIGN(Subprocess);
 };
 
-Subprocess::Subprocess(const std::string& command, const char* terminal_type,
-                       SubprocessType type, SubprocessProtocol protocol)
-    : command_(command),
+Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+                       SubprocessProtocol protocol, bool make_pty_raw)
+    : command_(std::move(command)),
       terminal_type_(terminal_type ? terminal_type : ""),
       type_(type),
-      protocol_(protocol) {
-    // If we aren't using the shell protocol we must allocate a PTY to properly close the
-    // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
-    // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
-    // e.g. screenrecord, will never notice the broken pipe and terminate.
-    // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
-    // with select() and will send SIGHUP manually to the child process.
-    if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
-        // Disable PTY input/output processing since the client is expecting raw data.
-        D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
-        type_ = SubprocessType::kPty;
-        make_pty_raw_ = true;
-    }
-}
+      protocol_(protocol),
+      make_pty_raw_(make_pty_raw) {}
 
 Subprocess::~Subprocess() {
     WaitForExit();
@@ -430,6 +422,67 @@
     return true;
 }
 
+bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) {
+    unique_fd child_stdinout_sfd, child_stderr_sfd;
+
+    CHECK(type_ == SubprocessType::kRaw);
+    CHECK(protocol_ == SubprocessProtocol::kShell);
+
+    __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+
+    if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+        *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
+                                             strerror(errno));
+        return false;
+    }
+    // Raw subprocess + shell protocol allows for splitting stderr.
+    if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+        *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
+                                             strerror(errno));
+        return false;
+    }
+
+    D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
+      stderr_sfd_.get());
+
+    // Required for shell protocol: create another socketpair to intercept data.
+    if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
+        *error = android::base::StringPrintf("failed to create socketpair to intercept data: %s",
+                                             strerror(errno));
+        return false;
+    }
+    D("protocol FD = %d", protocol_sfd_.get());
+
+    input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+    output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+    if (!input_ || !output_) {
+        *error = "failed to allocate shell protocol objects";
+        return false;
+    }
+
+    // Don't let reads/writes to the subprocess block our thread. This isn't
+    // likely but could happen under unusual circumstances, such as if we
+    // write a ton of data to stdin but the subprocess never reads it and
+    // the pipe fills up.
+    for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
+        if (fd >= 0) {
+            if (!set_file_block_mode(fd, false)) {
+                *error = android::base::StringPrintf("failed to set non-blocking mode for fd %d",
+                                                     fd);
+                return false;
+            }
+        }
+    }
+
+    std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
+                 command = std::move(command),
+                 args = command_]() { command(args, inout_sfd, inout_sfd, err_sfd); })
+            .detach();
+
+    D("execinprocess: completed");
+    return true;
+}
+
 bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
     Subprocess* raw = subprocess.release();
     std::thread(ThreadHandler, raw).detach();
@@ -512,7 +565,9 @@
                 // needed (e.g. SIGINT), pass those through the shell protocol
                 // and only fall back on this for unexpected closures.
                 D("protocol FD died, sending SIGHUP to pid %d", pid_);
-                kill(pid_, SIGHUP);
+                if (pid_ != -1) {
+                    kill(pid_, SIGHUP);
+                }
 
                 // We also need to close the pipes connected to the child process
                 // so that if it ignores SIGHUP and continues to write data it
@@ -682,7 +737,7 @@
     int exit_code = 1;
 
     D("waiting for pid %d", pid_);
-    while (true) {
+    while (pid_ != -1) {
         int status;
         if (pid_ == waitpid(pid_, &status, 0)) {
             D("post waitpid (pid=%d) status=%04x", pid_, status);
@@ -716,7 +771,7 @@
 }  // namespace
 
 // Create a pipe containing the error.
-static unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
+unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
     unique_fd read, write;
     if (!Pipe(&read, &write)) {
         PLOG(ERROR) << "failed to create pipe to report error";
@@ -745,23 +800,51 @@
     return read;
 }
 
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
                           SubprocessProtocol protocol) {
+    // If we aren't using the shell protocol we must allocate a PTY to properly close the
+    // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
+    // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
+    // e.g. screenrecord, will never notice the broken pipe and terminate.
+    // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
+    // with select() and will send SIGHUP manually to the child process.
+    bool make_pty_raw = false;
+    if (protocol == SubprocessProtocol::kNone && type == SubprocessType::kRaw) {
+        // Disable PTY input/output processing since the client is expecting raw data.
+        D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
+        type = SubprocessType::kPty;
+        make_pty_raw = true;
+    }
+
+    unique_fd error_fd;
+    unique_fd fd = StartSubprocess(std::move(name), terminal_type, type, protocol, make_pty_raw,
+                                   protocol, &error_fd);
+    if (fd == -1) {
+        return error_fd;
+    }
+    return fd;
+}
+
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol protocol, bool make_pty_raw,
+                          SubprocessProtocol error_protocol, unique_fd* error_fd) {
     D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
       type == SubprocessType::kRaw ? "raw" : "PTY",
-      protocol == SubprocessProtocol::kNone ? "none" : "shell",
-      terminal_type, name);
+      protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
 
-    auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+    auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+                                                   make_pty_raw);
     if (!subprocess) {
         LOG(ERROR) << "failed to allocate new subprocess";
-        return ReportError(protocol, "failed to allocate new subprocess");
+        *error_fd = ReportError(error_protocol, "failed to allocate new subprocess");
+        return {};
     }
 
     std::string error;
     if (!subprocess->ForkAndExec(&error)) {
         LOG(ERROR) << "failed to start subprocess: " << error;
-        return ReportError(protocol, error);
+        *error_fd = ReportError(error_protocol, error);
+        return {};
     }
 
     unique_fd local_socket(subprocess->ReleaseLocalSocket());
@@ -770,6 +853,40 @@
 
     if (!Subprocess::StartThread(std::move(subprocess), &error)) {
         LOG(ERROR) << "failed to start subprocess management thread: " << error;
+        *error_fd = ReportError(error_protocol, error);
+        return {};
+    }
+
+    return local_socket;
+}
+
+unique_fd StartCommandInProcess(std::string name, Command command) {
+    LOG(INFO) << "StartCommandInProcess(" << dump_hex(name.data(), name.size()) << ")";
+
+    constexpr auto terminal_type = "";
+    constexpr auto type = SubprocessType::kRaw;
+    constexpr auto protocol = SubprocessProtocol::kShell;
+    constexpr auto make_pty_raw = false;
+
+    auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+                                                   make_pty_raw);
+    if (!subprocess) {
+        LOG(ERROR) << "failed to allocate new subprocess";
+        return ReportError(protocol, "failed to allocate new subprocess");
+    }
+
+    std::string error;
+    if (!subprocess->ExecInProcess(std::move(command), &error)) {
+        LOG(ERROR) << "failed to start subprocess: " << error;
+        return ReportError(protocol, error);
+    }
+
+    unique_fd local_socket(subprocess->ReleaseLocalSocket());
+    D("inprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
+      subprocess->pid());
+
+    if (!Subprocess::StartThread(std::move(subprocess), &error)) {
+        LOG(ERROR) << "failed to start inprocess management thread: " << error;
         return ReportError(protocol, error);
     }
 
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 2a48923..fc66377 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -16,8 +16,12 @@
 
 #pragma once
 
+#include <string>
+
 #include "adb_unique_fd.h"
 
+#include <string_view>
+
 enum class SubprocessType {
     kPty,
     kRaw,
@@ -32,5 +36,20 @@
 // shell is started, otherwise |name| is executed non-interactively.
 //
 // Returns an open FD connected to the subprocess or -1 on failure.
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
                           SubprocessProtocol protocol);
+
+// The same as above but with more fined grained control and custom error handling.
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol protocol, bool make_pty_raw,
+                          SubprocessProtocol error_protocol, unique_fd* error_fd);
+
+// Executes |command| in a separate thread.
+// Sets up in/out and error streams to emulate shell-like behavior.
+//
+// Returns an open FD connected to the thread or -1 on failure.
+using Command = int(std::string_view args, int in, int out, int err);
+unique_fd StartCommandInProcess(std::string name, Command command);
+
+// Create a pipe containing the error.
+unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
diff --git a/adb/services.cpp b/adb/services.cpp
index 4b033bd..8636657 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -71,7 +71,7 @@
     return unique_fd(s[0]);
 }
 
-int service_to_fd(const char* name, atransport* transport) {
+int service_to_fd(std::string_view name, atransport* transport) {
     int ret = -1;
 
     if (is_socket_spec(name)) {
diff --git a/adb/socket.h b/adb/socket.h
index 0905aab..e7df991 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -103,17 +103,18 @@
 void close_all_sockets(atransport *t);
 
 asocket *create_local_socket(int fd);
-asocket* create_local_service_socket(const char* destination, atransport* transport);
+asocket* create_local_service_socket(std::string_view destination, atransport* transport);
 
 asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
+void connect_to_remote(asocket* s, std::string_view destination);
 void connect_to_smartsocket(asocket *s);
 
 // Internal functions that are only made available here for testing purposes.
 namespace internal {
 
 #if ADB_HOST
-char* skip_host_serial(char* service);
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+                        std::string_view service);
 #endif
 
 }  // namespace internal
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index eb4df97..4cddc84 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -17,6 +17,7 @@
 #include "socket_spec.h"
 
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
@@ -29,7 +30,8 @@
 #include "adb.h"
 #include "sysdeps.h"
 
-using android::base::StartsWith;
+using namespace std::string_literals;
+
 using android::base::StringPrintf;
 
 #if defined(__linux__)
@@ -64,10 +66,11 @@
     { "localfilesystem", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
 });
 
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
                            std::string* error) {
-    if (!StartsWith(spec, "tcp:")) {
-        *error = StringPrintf("specification is not tcp: '%s'", spec.c_str());
+    if (!spec.starts_with("tcp:")) {
+        *error = "specification is not tcp: ";
+        *error += spec;
         return false;
     }
 
@@ -84,7 +87,7 @@
             return false;
         }
     } else {
-        std::string addr = spec.substr(4);
+        std::string addr(spec.substr(4));
         port_value = -1;
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
@@ -94,7 +97,8 @@
         }
 
         if (port_value == -1) {
-            *error = StringPrintf("missing port in specification: '%s'", spec.c_str());
+            *error = "missing port in specification: ";
+            *error += spec;
             return false;
         }
     }
@@ -110,25 +114,25 @@
     return true;
 }
 
-static bool tcp_host_is_local(const std::string& hostname) {
+static bool tcp_host_is_local(std::string_view hostname) {
     // FIXME
     return hostname.empty() || hostname == "localhost";
 }
 
-bool is_socket_spec(const std::string& spec) {
+bool is_socket_spec(std::string_view spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             return true;
         }
     }
-    return StartsWith(spec, "tcp:");
+    return spec.starts_with("tcp:");
 }
 
-bool is_local_socket_spec(const std::string& spec) {
+bool is_local_socket_spec(std::string_view spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             return true;
         }
     }
@@ -141,8 +145,8 @@
     return tcp_host_is_local(hostname);
 }
 
-int socket_spec_connect(const std::string& spec, std::string* error) {
-    if (StartsWith(spec, "tcp:")) {
+int socket_spec_connect(std::string_view spec, std::string* error) {
+    if (spec.starts_with("tcp:")) {
         std::string hostname;
         int port;
         if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
@@ -170,7 +174,7 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("socket type %s is unavailable on this platform",
                                       it.first.c_str());
@@ -182,12 +186,13 @@
         }
     }
 
-    *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+    *error = "unknown socket specification: ";
+    *error += spec;
     return -1;
 }
 
-int socket_spec_listen(const std::string& spec, std::string* error, int* resolved_tcp_port) {
-    if (StartsWith(spec, "tcp:")) {
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port) {
+    if (spec.starts_with("tcp:")) {
         std::string hostname;
         int port;
         if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
@@ -213,10 +218,10 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix)) {
+        if (spec.starts_with(prefix)) {
             if (!it.second.available) {
-                *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
-                                      spec.c_str());
+                *error = "attempted to listen on unavailable socket type: ";
+                *error += spec;
                 return -1;
             }
 
@@ -225,6 +230,7 @@
         }
     }
 
-    *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+    *error = "unknown socket specification:";
+    *error += spec;
     return -1;
 }
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 6920e91..5b06973 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -19,13 +19,12 @@
 #include <string>
 
 // Returns true if the argument starts with a plausible socket prefix.
-bool is_socket_spec(const std::string& spec);
-bool is_local_socket_spec(const std::string& spec);
+bool is_socket_spec(std::string_view spec);
+bool is_local_socket_spec(std::string_view spec);
 
-int socket_spec_connect(const std::string& spec, std::string* error);
-int socket_spec_listen(const std::string& spec, std::string* error,
-                       int* resolved_tcp_port = nullptr);
+int socket_spec_connect(std::string_view spec, std::string* error);
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
 
 // Exposed for testing.
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
                            std::string* error);
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 04214a2..80f9430 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -34,6 +34,9 @@
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
 
+using namespace std::string_literals;
+using namespace std::string_view_literals;
+
 struct ThreadArg {
     int first_read_fd;
     int last_write_fd;
@@ -303,56 +306,78 @@
 
 #if ADB_HOST
 
-// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
-// |expected|, otherwise logs the failure to gtest.
-void VerifySkipHostSerial(std::string serial, const char* expected) {
-    char* result = internal::skip_host_serial(&serial[0]);
-    if (expected == nullptr) {
-        EXPECT_EQ(nullptr, result);
-    } else {
-        EXPECT_STREQ(expected, result);
-    }
-}
+#define VerifyParseHostServiceFailed(s)                                         \
+    do {                                                                        \
+        std::string service(s);                                                 \
+        std::string_view serial, command;                                       \
+        bool result = internal::parse_host_service(&serial, &command, service); \
+        EXPECT_FALSE(result);                                                   \
+    } while (0)
+
+#define VerifyParseHostService(s, expected_serial, expected_command)            \
+    do {                                                                        \
+        std::string service(s);                                                 \
+        std::string_view serial, command;                                       \
+        bool result = internal::parse_host_service(&serial, &command, service); \
+        EXPECT_TRUE(result);                                                    \
+        EXPECT_EQ(std::string(expected_serial), std::string(serial));           \
+        EXPECT_EQ(std::string(expected_command), std::string(command));         \
+    } while (0);
 
 // Check [tcp:|udp:]<serial>[:<port>]:<command> format.
-TEST(socket_test, test_skip_host_serial) {
+TEST(socket_test, test_parse_host_service) {
     for (const std::string& protocol : {"", "tcp:", "udp:"}) {
-        VerifySkipHostSerial(protocol, nullptr);
-        VerifySkipHostSerial(protocol + "foo", nullptr);
+        VerifyParseHostServiceFailed(protocol);
+        VerifyParseHostServiceFailed(protocol + "foo");
 
-        VerifySkipHostSerial(protocol + "foo:bar", ":bar");
-        VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+        {
+            std::string serial = protocol + "foo";
+            VerifyParseHostService(serial + ":bar", serial, "bar");
+            VerifyParseHostService(serial + " :bar:baz", serial, "bar:baz");
+        }
 
-        VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
-        VerifySkipHostSerial(protocol + "foo:123:456", ":456");
-        VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");
+        {
+            // With port.
+            std::string serial = protocol + "foo:123";
+            VerifyParseHostService(serial + ":bar", serial, "bar");
+            VerifyParseHostService(serial + ":456", serial, "456");
+            VerifyParseHostService(serial + ":bar:baz", serial, "bar:baz");
+        }
 
         // Don't register a port unless it's all numbers and ends with ':'.
-        VerifySkipHostSerial(protocol + "foo:123", ":123");
-        VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
+        VerifyParseHostService(protocol + "foo:123", protocol + "foo", "123");
+        VerifyParseHostService(protocol + "foo:123bar:baz", protocol + "foo", "123bar:baz");
 
-        VerifySkipHostSerial(protocol + "100.100.100.100:5555:foo", ":foo");
-        VerifySkipHostSerial(protocol + "[0123:4567:89ab:CDEF:0:9:a:f]:5555:foo", ":foo");
-        VerifySkipHostSerial(protocol + "[::1]:5555:foo", ":foo");
+        std::string addresses[] = {"100.100.100.100", "[0123:4567:89ab:CDEF:0:9:a:f]", "[::1]"};
+        for (const std::string& address : addresses) {
+            std::string serial = protocol + address;
+            std::string serial_with_port = protocol + address + ":5555";
+            VerifyParseHostService(serial + ":foo", serial, "foo");
+            VerifyParseHostService(serial_with_port + ":foo", serial_with_port, "foo");
+        }
 
         // If we can't find both [] then treat it as a normal serial with [ in it.
-        VerifySkipHostSerial(protocol + "[0123:foo", ":foo");
+        VerifyParseHostService(protocol + "[0123:foo", protocol + "[0123", "foo");
 
         // Don't be fooled by random IPv6 addresses in the command string.
-        VerifySkipHostSerial(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
-                             ":ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+        VerifyParseHostService(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
+                               protocol + "foo", "ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+
+        // Handle embedded NULs properly.
+        VerifyParseHostService(protocol + "foo:echo foo\0bar"s, protocol + "foo",
+                               "echo foo\0bar"sv);
     }
 }
 
 // Check <prefix>:<serial>:<command> format.
-TEST(socket_test, test_skip_host_serial_prefix) {
+TEST(socket_test, test_parse_host_service_prefix) {
     for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
-        VerifySkipHostSerial(prefix, nullptr);
-        VerifySkipHostSerial(prefix + "foo", nullptr);
+        VerifyParseHostServiceFailed(prefix);
+        VerifyParseHostServiceFailed(prefix + "foo");
 
-        VerifySkipHostSerial(prefix + "foo:bar", ":bar");
-        VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
-        VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+        VerifyParseHostService(prefix + "foo:bar", prefix + "foo", "bar");
+        VerifyParseHostService(prefix + "foo:bar:baz", prefix + "foo", "bar:baz");
+        VerifyParseHostService(prefix + "foo:123:bar", prefix + "foo", "123:bar");
     }
 }
 
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 1bd57c1..47ae883 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -37,6 +37,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "transport.h"
 #include "types.h"
 
@@ -346,7 +347,7 @@
     return s;
 }
 
-asocket* create_local_service_socket(const char* name, atransport* transport) {
+asocket* create_local_service_socket(std::string_view name, atransport* transport) {
 #if !ADB_HOST
     if (asocket* s = daemon_service_to_socket(name); s) {
         return s;
@@ -358,13 +359,12 @@
     }
 
     asocket* s = create_local_socket(fd);
-    D("LS(%d): bound to '%s' via %d", s->id, name, fd);
+    LOG(VERBOSE) << "LS(" << s->id << "): bound to '" << name << "' via " << fd;
 
 #if !ADB_HOST
-    if ((!strncmp(name, "root:", 5) && getuid() != 0 && __android_log_is_debuggable()) ||
-        (!strncmp(name, "unroot:", 7) && getuid() == 0) ||
-        !strncmp(name, "usb:", 4) ||
-        !strncmp(name, "tcpip:", 6)) {
+    if ((name.starts_with("root:") && getuid() != 0 && __android_log_is_debuggable()) ||
+        (name.starts_with("unroot:") && getuid() == 0) || name.starts_with("usb:") ||
+        name.starts_with("tcpip:")) {
         D("LS(%d): enabling exit_on_close", s->id);
         s->exit_on_close = 1;
     }
@@ -462,16 +462,19 @@
     return s;
 }
 
-void connect_to_remote(asocket* s, const char* destination) {
+void connect_to_remote(asocket* s, std::string_view destination) {
     D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
     apacket* p = get_apacket();
 
-    D("LS(%d): connect('%s')", s->id, destination);
+    LOG(VERBOSE) << "LS(" << s->id << ": connect(" << destination << ")";
     p->msg.command = A_OPEN;
     p->msg.arg0 = s->id;
 
-    // adbd expects a null-terminated string.
-    p->payload.assign(destination, destination + strlen(destination) + 1);
+    // adbd used to expect a null-terminated string.
+    // Keep doing so to maintain backward compatibility.
+    p->payload.resize(destination.size() + 1);
+    memcpy(p->payload.data(), destination.data(), destination.size());
+    p->payload[destination.size()] = '\0';
     p->msg.data_length = p->payload.size();
 
     CHECK_LE(p->msg.data_length, s->get_max_payload());
@@ -547,57 +550,119 @@
 
 namespace internal {
 
-// Returns the position in |service| following the target serial parameter. Serial format can be
-// any of:
+// Parses a host service string of the following format:
 //   * [tcp:|udp:]<serial>[:<port>]:<command>
 //   * <prefix>:<serial>:<command>
 // Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
-//
-// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
-char* skip_host_serial(char* service) {
-    static const std::vector<std::string>& prefixes =
-        *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+                        std::string_view full_service) {
+    if (full_service.empty()) {
+        return false;
+    }
 
-    for (const std::string& prefix : prefixes) {
-        if (!strncmp(service, prefix.c_str(), prefix.length())) {
-            return strchr(service + prefix.length(), ':');
+    std::string_view serial;
+    std::string_view command = full_service;
+    // Remove |count| bytes from the beginning of command and add them to |serial|.
+    auto consume = [&full_service, &serial, &command](size_t count) {
+        CHECK_LE(count, command.size());
+        if (!serial.empty()) {
+            CHECK_EQ(serial.data() + serial.size(), command.data());
+        }
+
+        serial = full_service.substr(0, serial.size() + count);
+        command.remove_prefix(count);
+    };
+
+    // Remove the trailing : from serial, and assign the values to the output parameters.
+    auto finish = [out_serial, out_command, &serial, &command] {
+        if (serial.empty() || command.empty()) {
+            return false;
+        }
+
+        CHECK_EQ(':', serial.back());
+        serial.remove_suffix(1);
+
+        *out_serial = serial;
+        *out_command = command;
+        return true;
+    };
+
+    static constexpr std::string_view prefixes[] = {"usb:", "product:", "model:", "device:"};
+    for (std::string_view prefix : prefixes) {
+        if (command.starts_with(prefix)) {
+            consume(prefix.size());
+
+            size_t offset = command.find_first_of(':');
+            if (offset == std::string::npos) {
+                return false;
+            }
+            consume(offset + 1);
+            return finish();
         }
     }
 
     // For fastboot compatibility, ignore protocol prefixes.
-    if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
-        service += 4;
-    }
-
-    // Check for an IPv6 address. `adb connect` creates the serial number from the canonical
-    // network address so it will always have the [] delimiters.
-    if (service[0] == '[') {
-        char* ipv6_end = strchr(service, ']');
-        if (ipv6_end != nullptr) {
-            service = ipv6_end;
+    if (command.starts_with("tcp:") || command.starts_with("udp:")) {
+        consume(4);
+        if (command.empty()) {
+            return false;
         }
     }
 
-    // The next colon we find must either begin the port field or the command field.
-    char* colon_ptr = strchr(service, ':');
-    if (!colon_ptr) {
-        // No colon in service string.
-        return nullptr;
+    bool found_address = false;
+    if (command[0] == '[') {
+        // Read an IPv6 address. `adb connect` creates the serial number from the canonical
+        // network address so it will always have the [] delimiters.
+        size_t ipv6_end = command.find_first_of(']');
+        if (ipv6_end != std::string::npos) {
+            consume(ipv6_end + 1);
+            if (command.empty()) {
+                // Nothing after the IPv6 address.
+                return false;
+            } else if (command[0] != ':') {
+                // Garbage after the IPv6 address.
+                return false;
+            }
+            consume(1);
+            found_address = true;
+        }
     }
 
-    // If the next field is only decimal digits and ends with another colon, it's a port.
-    char* serial_end = colon_ptr;
-    if (isdigit(serial_end[1])) {
-        serial_end++;
-        while (*serial_end && isdigit(*serial_end)) {
-            serial_end++;
+    if (!found_address) {
+        // Scan ahead to the next colon.
+        size_t offset = command.find_first_of(':');
+        if (offset == std::string::npos) {
+            return false;
         }
-        if (*serial_end != ':') {
-            // Something other than "<port>:" was found, this must be the command field instead.
-            serial_end = colon_ptr;
+        consume(offset + 1);
+    }
+
+    // We're either at the beginning of a port, or the command itself.
+    // Look for a port in between colons.
+    size_t next_colon = command.find_first_of(':');
+    if (next_colon == std::string::npos) {
+        // No colon, we must be at the command.
+        return finish();
+    }
+
+    bool port_valid = true;
+    if (command.size() <= next_colon) {
+        return false;
+    }
+
+    std::string_view port = command.substr(0, next_colon);
+    for (auto digit : port) {
+        if (!isdigit(digit)) {
+            // Port isn't a number.
+            port_valid = false;
+            break;
         }
     }
-    return serial_end;
+
+    if (port_valid) {
+        consume(next_colon + 1);
+    }
+    return finish();
 }
 
 }  // namespace internal
@@ -606,8 +671,8 @@
 
 static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
 #if ADB_HOST
-    char* service = nullptr;
-    char* serial = nullptr;
+    std::string_view service;
+    std::string_view serial;
     TransportId transport_id = 0;
     TransportType type = kTransportAny;
 #endif
@@ -644,49 +709,52 @@
     D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
 
 #if ADB_HOST
-    service = &s->smart_socket_data[4];
-    if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
-        char* serial_end;
-        service += strlen("host-serial:");
+    service = std::string_view(s->smart_socket_data).substr(4);
+    if (service.starts_with("host-serial:")) {
+        service.remove_prefix(strlen("host-serial:"));
 
         // serial number should follow "host:" and could be a host:port string.
-        serial_end = internal::skip_host_serial(service);
-        if (serial_end) {
-            *serial_end = 0;  // terminate string
-            serial = service;
-            service = serial_end + 1;
+        if (!internal::parse_host_service(&serial, &service, service)) {
+            LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
+            goto fail;
         }
-    } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
-        service += strlen("host-transport-id:");
-        transport_id = strtoll(service, &service, 10);
-
-        if (*service != ':') {
+    } else if (service.starts_with("host-transport-id:")) {
+        service.remove_prefix(strlen("host-transport-id:"));
+        if (!ParseUint(&transport_id, service, &service)) {
+            LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
             return -1;
         }
-        service++;
-    } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+        if (!service.starts_with(":")) {
+            LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
+            return -1;
+        }
+        service.remove_prefix(1);
+    } else if (service.starts_with("host-usb:")) {
         type = kTransportUsb;
-        service += strlen("host-usb:");
-    } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+        service.remove_prefix(strlen("host-usb:"));
+    } else if (service.starts_with("host-local:")) {
         type = kTransportLocal;
-        service += strlen("host-local:");
-    } else if (!strncmp(service, "host:", strlen("host:"))) {
+        service.remove_prefix(strlen("host-local:"));
+    } else if (service.starts_with("host:")) {
         type = kTransportAny;
-        service += strlen("host:");
+        service.remove_prefix(strlen("host:"));
     } else {
-        service = nullptr;
+        service = std::string_view{};
     }
 
-    if (service) {
+    if (!service.empty()) {
         asocket* s2;
 
         // Some requests are handled immediately -- in that case the handle_host_request() routine
         // has sent the OKAY or FAIL message and all we have to do is clean up.
-        if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s)) {
-            D("SS(%d): handled host service '%s'", s->id, service);
+        // TODO: Convert to string_view.
+        if (handle_host_request(std::string(service).c_str(), type,
+                                serial.empty() ? nullptr : std::string(serial).c_str(),
+                                transport_id, s->peer->fd, s)) {
+            LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
             goto fail;
         }
-        if (!strncmp(service, "transport", strlen("transport"))) {
+        if (service.starts_with("transport")) {
             D("SS(%d): okay transport", s->id);
             s->smart_socket_data.clear();
             return 0;
@@ -696,9 +764,11 @@
         ** if no such service exists, we'll fail out
         ** and tear down here.
         */
-        s2 = create_host_service_socket(service, serial, transport_id);
+        // TODO: Convert to string_view.
+        s2 = create_host_service_socket(std::string(service).c_str(), std::string(serial).c_str(),
+                                        transport_id);
         if (s2 == nullptr) {
-            D("SS(%d): couldn't create host service '%s'", s->id, service);
+            LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'";
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
         }
@@ -759,7 +829,7 @@
     /* give him our transport and upref it */
     s->peer->transport = s->transport;
 
-    connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
+    connect_to_remote(s->peer, std::string_view(s->smart_socket_data).substr(4));
     s->peer = nullptr;
     s->close(s);
     return 1;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index b8d7e06..15247e7 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -27,6 +27,7 @@
 #include <errno.h>
 
 #include <string>
+#include <string_view>
 #include <vector>
 
 // Include this before open/close/unlink are defined as macros below.
@@ -139,7 +140,7 @@
 }
 
 // See the comments for the !defined(_WIN32) version of unix_open().
-extern int unix_open(const char* path, int options, ...);
+extern int unix_open(std::string_view path, int options, ...);
 #define  open    ___xxx_unix_open
 
 // Checks if |fd| corresponds to a console.
@@ -357,20 +358,17 @@
 // by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
 // configurable CR/LF translation which defaults to text mode, but is settable
 // with _setmode().
-static __inline__ int  unix_open(const char*  path, int options,...)
-{
-    if ((options & O_CREAT) == 0)
-    {
-        return  TEMP_FAILURE_RETRY( open(path, options) );
-    }
-    else
-    {
-        int      mode;
-        va_list  args;
-        va_start( args, options );
-        mode = va_arg( args, int );
-        va_end( args );
-        return TEMP_FAILURE_RETRY( open( path, options, mode ) );
+static __inline__ int unix_open(std::string_view path, int options, ...) {
+    std::string zero_terminated(path.begin(), path.end());
+    if ((options & O_CREAT) == 0) {
+        return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options));
+    } else {
+        int mode;
+        va_list args;
+        va_start(args, options);
+        mode = va_arg(args, int);
+        va_end(args);
+        return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options, mode));
     }
 }
 
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 8a6541d..dbc8920 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -29,6 +29,7 @@
 #include <memory>
 #include <mutex>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
@@ -2203,15 +2204,15 @@
     }
 }
 
-int unix_open(const char* path, int options, ...) {
+int unix_open(std::string_view path, int options, ...) {
     std::wstring path_wide;
-    if (!android::base::UTF8ToWide(path, &path_wide)) {
+    if (!android::base::UTF8ToWide(path.data(), path.size(), &path_wide)) {
         return -1;
     }
     if ((options & O_CREAT) == 0) {
         return _wopen(path_wide.c_str(), options);
     } else {
-        int      mode;
+        int mode;
         va_list  args;
         va_start(args, options);
         mode = va_arg(args, int);
diff --git a/base/Android.bp b/base/Android.bp
index 741664b..b0181aa 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,6 +20,7 @@
         "-Wall",
         "-Werror",
         "-Wextra",
+        "-D_FILE_OFFSET_BITS=64",
     ],
 }
 
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d9fae52..10f52f4 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -116,14 +116,21 @@
         "libdebuggerd",
         "libbacktrace",
         "libunwindstack",
-        "libdexfile",
+        "libdexfile",  // libunwindstack dependency
+        "libdexfile_external",  // libunwindstack dependency
+        "libdexfile_support",  // libunwindstack dependency
         "liblzma",
         "libcutils",
     ],
     target: {
         recovery: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_static_libs: ["libdexfile"],
+            exclude_static_libs: [
+                "libartbase",
+                "libdexfile",
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
         },
     },
 
@@ -143,6 +150,7 @@
     shared_libs: [
         "libbase",
         "libcutils",
+        "libprocinfo",
     ],
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
@@ -170,12 +178,22 @@
 
     static_libs: [
         "libbacktrace",
+        "libdexfile_external",  // libunwindstack dependency
+        "libdexfile_support",  // libunwindstack dependency
         "libunwindstack",
         "liblzma",
         "libbase",
         "libcutils",
         "liblog",
     ],
+    target: {
+        recovery: {
+            exclude_static_libs: [
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
+        },
+    },
 }
 
 cc_test {
@@ -216,6 +234,8 @@
 
     static_libs: [
         "libdebuggerd",
+        "libdexfile_external",  // libunwindstack dependency
+        "libdexfile_support",  // libunwindstack dependency
         "libunwindstack",
     ],
 
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 77f3515..785df99 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -33,6 +33,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
+#include <procinfo/process.h>
 
 #include "debuggerd/handler.h"
 #include "protocol.h"
@@ -62,8 +63,20 @@
   tv->tv_usec = static_cast<long>(microseconds.count());
 }
 
-bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
                             unique_fd output_fd) {
+  pid_t pid = tid;
+  if (dump_type == kDebuggerdJavaBacktrace) {
+    // Java dumps always get sent to the tgid, so we need to resolve our tid to a tgid.
+    android::procinfo::ProcessInfo procinfo;
+    std::string error;
+    if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {
+      LOG(ERROR) << "libdebugged_client: failed to get process info: " << error;
+      return -1;
+    }
+    pid = procinfo.pid;
+  }
+
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
   unique_fd sockfd;
   const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -162,7 +175,7 @@
     return false;
   }
 
-  if (!send_signal(pid, dump_type)) {
+  if (!send_signal(tid, dump_type)) {
     return false;
   }
 
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 963916c..f737405 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -143,6 +143,11 @@
         return device->WriteFail("Data is not a valid logical partition metadata image");
     }
 
+    if (!FindPhysicalPartition(super_name)) {
+        return device->WriteFail("Cannot find " + super_name +
+                                 ", build may be missing broken or missing boot_devices");
+    }
+
     // If we are unable to read the existing metadata, then the super partition
     // is corrupt. In this case we reflash the whole thing using the provided
     // image.
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index ac138a3..824511e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -40,6 +40,7 @@
         "fs_mgr_verity.cpp",
         "fs_mgr_dm_linear.cpp",
         "fs_mgr_overlayfs.cpp",
+        "fs_mgr_roots.cpp",
         "fs_mgr_vendor_overlay.cpp",
     ],
     shared_libs: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 88f7a2c..943fe10 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1179,11 +1179,12 @@
 
 // wrapper to __mount() and expects a fully prepared fstab_rec,
 // unlike fs_mgr_do_mount which does more things with avb / verity etc.
-int fs_mgr_do_mount_one(const FstabEntry& entry) {
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
     // Run fsck if needed
     prepare_fs_for_mount(entry.blk_device, entry);
 
-    int ret = __mount(entry.blk_device, entry.mount_point, entry);
+    int ret =
+            __mount(entry.blk_device, mount_point.empty() ? entry.mount_point : mount_point, entry);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 8bfcd81..ef7405a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -751,8 +751,14 @@
     auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
     if (partition_exists) {
         if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
-            if (change) *change = true;
-            return true;
+            if (!fs_mgr_access(kScratchMountPoint + kOverlayTopDir) &&
+                !fs_mgr_filesystem_has_space(kScratchMountPoint)) {
+                // declare it useless, no overrides and no free space
+                fs_mgr_overlayfs_umount_scratch();
+            } else {
+                if (change) *change = true;
+                return true;
+            }
         }
         // partition existed, but was not initialized; fall through to make it.
         errno = 0;
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
new file mode 100644
index 0000000..32a5d21
--- /dev/null
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2018 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 "fs_mgr/roots.h"
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
+#include "fs_mgr_priv.h"
+
+namespace android {
+namespace fs_mgr {
+
+static constexpr const char* kSystemRoot = "/system";
+
+static bool gDidMapLogicalPartitions = false;
+
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path) {
+    if (path.empty()) return nullptr;
+    std::string str(path);
+    while (true) {
+        auto it = std::find_if(fstab->begin(), fstab->end(),
+                               [&str](const auto& entry) { return entry.mount_point == str; });
+        if (it != fstab->end()) return &*it;
+        if (str == "/") break;
+        auto slash = str.find_last_of('/');
+        if (slash == std::string::npos) break;
+        if (slash == 0) {
+            str = "/";
+        } else {
+            str = str.substr(0, slash);
+        }
+    }
+    return nullptr;
+}
+
+enum class MountState {
+    ERROR = -1,
+    NOT_MOUNTED = 0,
+    MOUNTED = 1,
+};
+
+static MountState GetMountState(const std::string& mount_point) {
+    Fstab mounted_fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+        LERROR << "Failed to scan mounted volumes";
+        return MountState::ERROR;
+    }
+
+    auto mv = std::find_if(
+            mounted_fstab.begin(), mounted_fstab.end(),
+            [&mount_point](const auto& entry) { return entry.mount_point == mount_point; });
+    if (mv != mounted_fstab.end()) {
+        return MountState::MOUNTED;
+    }
+    return MountState::NOT_MOUNTED;
+}
+
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
+    auto rec = GetEntryForPath(fstab, path);
+    if (rec == nullptr) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+    if (rec->fs_type == "ramdisk") {
+        // The ramdisk is always mounted.
+        return true;
+    }
+
+    // If we can't acquire the block device for a logical partition, it likely
+    // was never created. In that case we try to create it.
+    if (rec->fs_mgr_flags.logical && !fs_mgr_update_logical_partition(rec)) {
+        if (gDidMapLogicalPartitions) {
+            LERROR << "Failed to find block device for partition";
+            return false;
+        }
+        std::string super_name = fs_mgr_get_super_partition_name();
+        if (!android::fs_mgr::CreateLogicalPartitions("/dev/block/by-name/" + super_name)) {
+            LERROR << "Failed to create logical partitions";
+            return false;
+        }
+        gDidMapLogicalPartitions = true;
+        if (!fs_mgr_update_logical_partition(rec)) {
+            LERROR << "Failed to find block device for partition";
+            return false;
+        }
+    }
+
+    auto mounted = GetMountState(rec->mount_point);
+    if (mounted == MountState::ERROR) {
+        return false;
+    }
+    if (mounted == MountState::MOUNTED) {
+        return true;
+    }
+
+    const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
+
+    static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
+    if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
+        LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
+        return false;
+    }
+
+    int result = fs_mgr_do_mount_one(*rec, mount_point);
+    if (result == -1 && rec->fs_mgr_flags.formattable) {
+        PERROR << "Failed to mount " << mount_point << "; formatting";
+        bool crypt_footer = rec->is_encryptable() && rec->key_loc == "footer";
+        if (fs_mgr_do_format(*rec, crypt_footer) != 0) {
+            PERROR << "Failed to format " << mount_point;
+            return false;
+        }
+        result = fs_mgr_do_mount_one(*rec, mount_point);
+    }
+
+    if (result == -1) {
+        PERROR << "Failed to mount " << mount_point;
+        return false;
+    }
+    return true;
+}
+
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
+    auto rec = GetEntryForPath(fstab, path);
+    if (rec == nullptr) {
+        LERROR << "unknown volume for path [" << path << "]";
+        return false;
+    }
+    if (rec->fs_type == "ramdisk") {
+        // The ramdisk is always mounted; you can't unmount it.
+        return false;
+    }
+
+    Fstab mounted_fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+        LERROR << "Failed to scan mounted volumes";
+        return false;
+    }
+
+    auto mounted = GetMountState(rec->mount_point);
+    if (mounted == MountState::ERROR) {
+        return false;
+    }
+    if (mounted == MountState::NOT_MOUNTED) {
+        return true;
+    }
+
+    int result = umount(rec->mount_point.c_str());
+    if (result == -1) {
+        PWARNING << "Failed to umount " << rec->mount_point;
+        return false;
+    }
+    return true;
+}
+
+std::string GetSystemRoot() {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        LERROR << "Failed to read default fstab";
+        return "";
+    }
+
+    auto it = std::find_if(fstab.begin(), fstab.end(),
+                           [](const auto& entry) { return entry.mount_point == kSystemRoot; });
+    if (it == fstab.end()) {
+        return "/";
+    }
+
+    return kSystemRoot;
+}
+
+bool LogicalPartitionsMapped() {
+    return gDidMapLogicalPartitions;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 814ba46..e87332f 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -69,7 +69,7 @@
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
                     bool need_cp);
-int fs_mgr_do_mount_one(const FstabEntry& entry);
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point = "");
 int fs_mgr_do_mount_one(fstab_rec* rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab);
diff --git a/fs_mgr/include/fs_mgr/roots.h b/fs_mgr/include/fs_mgr/roots.h
new file mode 100644
index 0000000..65c59cf
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/roots.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_mgr.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match
+// only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log",
+// "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the
+// first match or nullptr.
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);
+
+// Make sure that the volume 'path' is on is mounted.
+// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call
+//   fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.
+// * If 'mount_point' is not nullptr, the mount point is overridden. Caller can
+//   call umount(mount_point) to unmount.
+// Returns true on success (volume is mounted).
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point = "");
+
+// Make sure that the volume 'path' is on is unmounted.  Returns true on
+// success (volume is unmounted).
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path);
+
+// Return "/system" if it is in default fstab, otherwise "/".
+std::string GetSystemRoot();
+
+// Return true iff logical partitions are mapped when partitions are mounted via ensure_path_mounted
+// functions.
+bool LogicalPartitionsMapped();
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 22af123..90bce51 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -20,10 +20,6 @@
     recovery_available: true,
 
     export_include_dirs: ["include"],
-    cflags: [
-        // TODO(b/110035986): Allows us to create a skeleton of required classes
-        "-Wno-unused-private-field",
-    ],
 
     srcs: [
         "dm_table.cpp",
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index c4b57c7..d9786ad 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -203,7 +203,8 @@
         }
         next += vers->next;
         data_size -= vers->next;
-        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
+                                                            next);
     }
 
     return true;
@@ -288,12 +289,23 @@
 }
 
 bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+    return GetTable(name, 0, table);
+}
+
+bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
+    return GetTable(name, DM_STATUS_TABLE_FLAG, table);
+}
+
+// private methods of DeviceMapper
+bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
+                            std::vector<TargetInfo>* table) {
     char buffer[4096];
     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);
 
     InitIo(io, name);
     io->data_size = sizeof(buffer);
     io->data_start = sizeof(*io);
+    io->flags = flags;
     if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
         PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
         return false;
@@ -327,7 +339,6 @@
     return true;
 }
 
-// private methods of DeviceMapper
 void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
     CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
     memset(io, 0, sizeof(*io));
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 91f8bb4..28e6e01 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -128,6 +128,10 @@
     };
     bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
 
+    // Identical to GetTableStatus, except also retrives the active table for the device
+    // mapper device from the kernel.
+    bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+
   private:
     // Maximum possible device mapper targets registered in the kernel.
     // This is only used to read the list of targets from kernel so we allocate
@@ -140,6 +144,8 @@
     // limit we are imposing here of 256.
     static constexpr uint32_t kMaxPossibleDmDevices = 256;
 
+    bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
+
     void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
 
     DeviceMapper();
diff --git a/fs_mgr/libfiemap_writer/.clang-format b/fs_mgr/libfiemap_writer/.clang-format
new file mode 120000
index 0000000..8b770a1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-4
\ No newline at end of file
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
new file mode 100644
index 0000000..33c3cad
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "libfiemap_writer",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+    export_include_dirs: ["include"],
+    cflags: [
+        // TODO(b/121211685): Allows us to create a skeleton of required classes
+        "-Wno-unused-private-field",
+        "-Wno-unused-parameter",
+    ],
+
+    srcs: [
+        "fiemap_writer.cpp",
+    ],
+
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
+}
+
+cc_test {
+    name: "fiemap_writer_test",
+    static_libs: [
+        "libbase",
+        "libdm",
+        "libfiemap_writer",
+        "liblog",
+    ],
+
+    data: [
+        "testdata/unaligned_file",
+        "testdata/file_4k",
+        "testdata/file_32k",
+    ],
+
+    srcs: [
+        "fiemap_writer_test.cpp",
+    ],
+}
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
new file mode 100644
index 0000000..4a0d4b5
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2018 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 <libfiemap_writer/fiemap_writer.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fiemap_writer {
+
+// We are expecting no more than 512 extents in a fiemap of the file we create.
+// If we find more, then it is treated as error for now.
+static constexpr const uint32_t kMaxExtents = 512;
+
+// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
+static constexpr const uint32_t kUnsupportedExtentFlags =
+        FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
+        FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
+        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
+
+static inline void cleanup(const std::string& file_path, bool created) {
+    if (created) {
+        unlink(file_path.c_str());
+    }
+}
+
+static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
+    // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
+    // The directory name in the target corresponds to the name of the block device. We use
+    // that to extract the block device name.
+    // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
+    // follows.
+    //    1:0 -> ../../devices/virtual/block/ram0
+    std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
+    std::string sysfs_bdev;
+
+    if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
+        PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
+        return false;
+    }
+
+    *bdev_name = ::android::base::Basename(sysfs_bdev);
+    // Paranoid sanity check to make sure we just didn't get the
+    // input in return as-is.
+    if (sysfs_bdev == *bdev_name) {
+        LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
+        return false;
+    }
+
+    return true;
+}
+
+static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
+    // TODO: Stop popping the device mapper stack if dm-linear target is found
+    if (!::android::base::StartsWith(bdev, "dm-")) {
+        // We are at the bottom of the device mapper stack.
+        *bdev_raw = bdev;
+        return true;
+    }
+
+    std::string dm_leaf_dir = ::android::base::StringPrintf("/sys/block/%s/slaves", bdev.c_str());
+    auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
+    if (d == nullptr) {
+        PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
+        return false;
+    }
+
+    struct dirent* de;
+    uint32_t num_leaves = 0;
+    std::string bdev_next = "";
+    while ((de = readdir(d.get())) != nullptr) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+            continue;
+        }
+
+        // We set the first name we find here
+        if (bdev_next.empty()) {
+            bdev_next = de->d_name;
+        }
+        num_leaves++;
+    }
+
+    // if we have more than one leaves, we return immediately. We can't continue to create the
+    // file since we don't know how to write it out using fiemap, so it will be readable via the
+    // underlying block devices later. The reader will also have to construct the same device mapper
+    // target in order read the file out.
+    if (num_leaves > 1) {
+        LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
+                   << bdev;
+        return false;
+    }
+
+    // recursively call with the block device we found in order to pop the device mapper stack.
+    return DeviceMapperStackPop(bdev_next, bdev_raw);
+}
+
+static bool FileToBlockDevicePath(const std::string& file_path, std::string* bdev_path) {
+    struct stat sb;
+    if (stat(file_path.c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get stat for: " << file_path;
+        return false;
+    }
+
+    std::string bdev;
+    if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
+        LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
+                   << minor(sb.st_dev);
+        return false;
+    }
+
+    std::string bdev_raw;
+    if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
+        LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
+        return false;
+    }
+
+    LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
+               << bdev << ")";
+
+    *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
+
+    // Make sure we are talking to a block device before calling it a success.
+    if (stat(bdev_path->c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
+        return false;
+    }
+
+    if ((sb.st_mode & S_IFMT) != S_IFBLK) {
+        PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
+        return false;
+    }
+
+    return true;
+}
+
+static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
+    uint64_t size_in_bytes = 0;
+    if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
+        PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
+        return false;
+    }
+
+    *bdev_size = size_in_bytes;
+
+    return true;
+}
+
+static uint64_t GetFileSize(const std::string& file_path) {
+    struct stat sb;
+    if (stat(file_path.c_str(), &sb)) {
+        PLOG(ERROR) << "Failed to get size for file: " << file_path;
+        return 0;
+    }
+
+    return sb.st_size;
+}
+
+static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
+                              uint32_t* fs_type) {
+    struct statfs64 sfs;
+    if (statfs64(file_path.c_str(), &sfs)) {
+        PLOG(ERROR) << "Failed to read file system status at: " << file_path;
+        return false;
+    }
+
+    if (file_size % sfs.f_bsize) {
+        LOG(ERROR) << "File size " << file_size << " is not aligned to optimal block size "
+                   << sfs.f_bsize << " for file " << file_path;
+        return false;
+    }
+
+    // Check if the filesystem is of supported types.
+    // Only ext4 and f2fs are tested and supported.
+    if ((sfs.f_type != EXT4_SUPER_MAGIC) && (sfs.f_type != F2FS_SUPER_MAGIC)) {
+        LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
+        return false;
+    }
+
+    uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
+    if (available_bytes <= file_size) {
+        LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
+        return false;
+    }
+
+    *blocksz = sfs.f_bsize;
+    *fs_type = sfs.f_type;
+    return true;
+}
+
+static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
+                         uint64_t file_size) {
+    // Reserve space for the file on the file system and write it out to make sure the extents
+    // don't come back unwritten. Return from this function with the kernel file offset set to 0.
+    // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
+    // aren't moved around.
+    if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
+        return false;
+    }
+
+    // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
+    // blocks are actually written to by the file system and thus getting rid of the holes in the
+    // file.
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory for writing file";
+        return false;
+    }
+
+    off64_t offset = lseek64(file_fd, 0, SEEK_SET);
+    if (offset < 0) {
+        PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
+        return false;
+    }
+
+    for (; offset < file_size; offset += blocksz) {
+        if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
+            PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
+                        << " in file " << file_path;
+            return false;
+        }
+    }
+
+    if (lseek64(file_fd, 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
+        return false;
+    }
+
+    // flush all writes here ..
+    if (fsync(file_fd)) {
+        PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
+        return false;
+    }
+
+    return true;
+}
+
+static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
+    if (fs_type == EXT4_SUPER_MAGIC) {
+        // No pinning necessary for ext4. The blocks, once allocated, are expected
+        // to be fixed.
+        return true;
+    }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_SET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#endif
+
+    uint32_t pin_status = 1;
+    int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
+    if (error < 0) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to pin file: " << file_path;
+        }
+        return false;
+    }
+
+    return true;
+}
+
+#if 0
+static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
+    if (fs_type == EXT4_SUPER_MAGIC) {
+        // No pinning necessary for ext4. The blocks, once allocated, are expected
+        // to be fixed.
+        return true;
+    }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_GET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+#endif
+
+    // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
+    uint32_t moved_blocks_nr;
+    int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
+    if (error < 0) {
+        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+            PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
+        } else {
+            PLOG(ERROR) << "Failed to get file pin status: " << file_path;
+        }
+        return false;
+    }
+
+    if (moved_blocks_nr) {
+        LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
+    }
+    return moved_blocks_nr == 0;
+}
+#endif
+
+static void LogExtent(uint32_t num, const struct fiemap_extent& ext) {
+    LOG(INFO) << "Extent #" << num;
+    LOG(INFO) << "  fe_logical:  " << ext.fe_logical;
+    LOG(INFO) << "  fe_physical: " << ext.fe_physical;
+    LOG(INFO) << "  fe_length:   " << ext.fe_length;
+    LOG(INFO) << "  fe_flags:    0x" << std::hex << ext.fe_flags;
+}
+
+static bool ReadFiemap(int file_fd, const std::string& file_path,
+                       std::vector<struct fiemap_extent>* extents) {
+    uint64_t fiemap_size =
+            sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "Failed to allocate memory for fiemap";
+        return false;
+    }
+
+    struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
+    fiemap->fm_start = 0;
+    fiemap->fm_length = UINT64_MAX;
+    // make sure file is synced to disk before we read the fiemap
+    fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+    fiemap->fm_extent_count = kMaxExtents;
+
+    if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
+        PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
+        return false;
+    }
+
+    if (fiemap->fm_mapped_extents == 0) {
+        LOG(ERROR) << "File " << file_path << " has zero extents";
+        return false;
+    }
+
+    // Iterate through each extent read and make sure its valid before adding it to the vector
+    bool last_extent_seen = false;
+    struct fiemap_extent* extent = &fiemap->fm_extents[0];
+    for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
+        // LogExtent(i + 1, *extent);
+        if (extent->fe_flags & kUnsupportedExtentFlags) {
+            LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
+                       << " has unsupported flags";
+            extents->clear();
+            return false;
+        }
+
+        if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
+            last_extent_seen = true;
+            if (i != (fiemap->fm_mapped_extents - 1)) {
+                LOG(WARNING) << "Extents are being received out-of-order";
+            }
+        }
+        extents->emplace_back(std::move(*extent));
+    }
+
+    if (!last_extent_seen) {
+        // The file is possibly too fragmented.
+        if (fiemap->fm_mapped_extents == kMaxExtents) {
+            LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
+        }
+        extents->clear();
+    }
+
+    return last_extent_seen;
+}
+
+FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create) {
+    // if 'create' is false, open an existing file and do not truncate.
+    int open_flags = O_RDWR | O_CLOEXEC;
+    if (create) {
+        if (access(file_path.c_str(), F_OK) == 0) {
+            LOG(WARNING) << "File " << file_path << " already exists, truncating";
+        }
+        open_flags |= O_CREAT | O_TRUNC;
+    }
+    ::android::base::unique_fd file_fd(
+            TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
+    if (file_fd < 0) {
+        PLOG(ERROR) << "Failed to create file at: " << file_path;
+        return nullptr;
+    }
+
+    std::string abs_path;
+    if (!::android::base::Realpath(file_path, &abs_path)) {
+        PLOG(ERROR) << "Invalid file path: " << file_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    std::string bdev_path;
+    if (!FileToBlockDevicePath(abs_path, &bdev_path)) {
+        LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
+        cleanup(abs_path, create);
+        return nullptr;
+    }
+
+    ::android::base::unique_fd bdev_fd(
+            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDWR | O_CLOEXEC)));
+    if (bdev_fd < 0) {
+        PLOG(ERROR) << "Failed to open block device: " << bdev_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    uint64_t bdevsz;
+    if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
+        LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
+        cleanup(file_path, create);
+        return nullptr;
+    }
+
+    if (!create) {
+        file_size = GetFileSize(abs_path);
+        if (file_size == 0) {
+            LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
+            return nullptr;
+        }
+    }
+
+    uint64_t blocksz;
+    uint32_t fs_type;
+    if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
+        LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
+        cleanup(abs_path, create);
+        return nullptr;
+    }
+
+    if (create) {
+        if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) {
+            cleanup(abs_path, create);
+            return nullptr;
+        }
+    }
+
+    // f2fs may move the file blocks around.
+    if (!PinFile(file_fd, file_path, fs_type)) {
+        cleanup(abs_path, create);
+        LOG(ERROR) << "Failed to pin the file in storage";
+        return nullptr;
+    }
+
+    // now allocate the FiemapWriter and start setting it up
+    FiemapUniquePtr fmap(new FiemapWriter());
+    if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
+        LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
+        cleanup(abs_path, create);
+        return nullptr;
+    }
+
+    fmap->file_path_ = abs_path;
+    fmap->bdev_path_ = bdev_path;
+    fmap->file_fd_ = std::move(file_fd);
+    fmap->bdev_fd_ = std::move(bdev_fd);
+    fmap->file_size_ = file_size;
+    fmap->bdev_size_ = bdevsz;
+    fmap->fs_type_ = fs_type;
+    fmap->block_size_ = blocksz;
+
+    LOG(INFO) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
+              << bdev_path;
+    return fmap;
+}
+
+bool FiemapWriter::Flush() const {
+    if (fsync(bdev_fd_)) {
+        PLOG(ERROR) << "Failed to flush " << bdev_path_ << " with fsync";
+        return false;
+    }
+    return true;
+}
+
+// TODO: Test with fs block_size > bdev block_size
+bool FiemapWriter::Write(off64_t off, uint8_t* buffer, uint64_t size) {
+    if (!size || size > file_size_) {
+        LOG(ERROR) << "Failed write: size " << size << " is invalid for file's size " << file_size_;
+        return false;
+    }
+
+    if (off + size > file_size_) {
+        LOG(ERROR) << "Failed write: Invalid offset " << off << " or size " << size
+                   << " for file size " << file_size_;
+        return false;
+    }
+
+    if ((off & (block_size_ - 1)) || (size & (block_size_ - 1))) {
+        LOG(ERROR) << "Failed write: Unaligned offset " << off << " or size " << size
+                   << " for block size " << block_size_;
+        return false;
+    }
+
+#if 0
+    // TODO(b/122138114): check why this fails.
+    if (!IsFilePinned(file_fd_, file_path_, fs_type_)) {
+        LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
+        return false;
+    }
+#endif
+    // find extents that must be written to and then write one at a time.
+    uint32_t num_extent = 1;
+    uint32_t buffer_offset = 0;
+    for (auto& extent : extents_) {
+        uint64_t e_start = extent.fe_logical;
+        uint64_t e_end = extent.fe_logical + extent.fe_length;
+        // Do we write in this extent ?
+        if (off >= e_start && off < e_end) {
+            uint64_t written = WriteExtent(extent, buffer + buffer_offset, off, size);
+            if (written == 0) {
+                return false;
+            }
+
+            buffer_offset += written;
+            off += written;
+            size -= written;
+
+            // Paranoid check to make sure we are done with this extent now
+            if (size && (off >= e_start && off < e_end)) {
+                LOG(ERROR) << "Failed to write extent fully";
+                LogExtent(num_extent, extent);
+                return false;
+            }
+
+            if (size == 0) {
+                // done
+                break;
+            }
+        }
+        num_extent++;
+    }
+
+    return true;
+}
+
+bool FiemapWriter::Read(off64_t off, uint8_t* buffer, uint64_t size) {
+    return false;
+}
+
+// private helpers
+
+// WriteExtent() Returns the total number of bytes written. It will always be multiple of
+// block_size_. 0 is returned in one of the two cases.
+//  1. Any write failed between logical_off & logical_off + length.
+//  2. The logical_offset + length doesn't overlap with the extent passed.
+// The function can either partially for fully write the extent depending on the
+// logical_off + length. It is expected that alignment checks for size and offset are
+// performed before calling into this function.
+uint64_t FiemapWriter::WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer,
+                                   off64_t logical_off, uint64_t length) {
+    uint64_t e_start = ext.fe_logical;
+    uint64_t e_end = ext.fe_logical + ext.fe_length;
+    if (logical_off < e_start || logical_off >= e_end) {
+        LOG(ERROR) << "Failed write extent, invalid offset " << logical_off << " and size "
+                   << length;
+        LogExtent(0, ext);
+        return 0;
+    }
+
+    off64_t bdev_offset = ext.fe_physical + (logical_off - e_start);
+    if (bdev_offset >= bdev_size_) {
+        LOG(ERROR) << "Failed write extent, invalid block # " << bdev_offset << " for block device "
+                   << bdev_path_ << " of size " << bdev_size_ << " bytes";
+        return 0;
+    }
+    if (TEMP_FAILURE_RETRY(lseek64(bdev_fd_, bdev_offset, SEEK_SET)) == -1) {
+        PLOG(ERROR) << "Failed write extent, seek offset for " << bdev_path_ << " offset "
+                    << bdev_offset;
+        return 0;
+    }
+
+    // Determine how much we want to write at once.
+    uint64_t logical_end = logical_off + length;
+    uint64_t write_size = (e_end <= logical_end) ? (e_end - logical_off) : length;
+    if (!android::base::WriteFully(bdev_fd_, buffer, write_size)) {
+        PLOG(ERROR) << "Failed write extent, write " << bdev_path_ << " at " << bdev_offset
+                    << " size " << write_size;
+        return 0;
+    }
+
+    return write_size;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
new file mode 100644
index 0000000..6dff0e8
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/loop_control.h>
+
+#include <libfiemap_writer/fiemap_writer.h>
+
+using namespace std;
+using namespace android::fiemap_writer;
+using unique_fd = android::base::unique_fd;
+using LoopDevice = android::dm::LoopDevice;
+
+std::string testbdev = "";
+uint64_t testfile_size = 536870912;  // default of 512MiB
+
+class FiemapWriterTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        std::string exec_dir = ::android::base::GetExecutableDirectory();
+        testfile = ::android::base::StringPrintf("%s/testdata/%s", exec_dir.c_str(), tinfo->name());
+    }
+
+    // name of the file we use for testing
+    std::string testfile;
+};
+
+TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
+    // Try creating a file of size ~100TB but aligned to
+    // 512 byte to make sure block alignment tests don't
+    // fail.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
+    EXPECT_EQ(fptr, nullptr);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+    EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_F(FiemapWriterTest, CreateUnalignedFile) {
+    // Try creating a file of size 4097 bytes which is guaranteed
+    // to be unaligned to all known block sizes. The creation must
+    // fail.
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4097);
+    EXPECT_EQ(fptr, nullptr);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+    EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_F(FiemapWriterTest, CheckFilePath) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+    ASSERT_NE(fptr, nullptr);
+    EXPECT_EQ(fptr->size(), 4096);
+    EXPECT_EQ(fptr->file_path(), testfile);
+    EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
+}
+
+TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+    EXPECT_EQ(fptr->size(), 4096);
+    EXPECT_EQ(fptr->bdev_path(), testbdev);
+}
+
+TEST_F(FiemapWriterTest, CheckFileCreated) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
+    ASSERT_NE(fptr, nullptr);
+    unique_fd fd(open(testfile.c_str(), O_RDONLY));
+    EXPECT_GT(fd, -1);
+}
+
+TEST_F(FiemapWriterTest, CheckFileSizeActual) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+
+    struct stat sb;
+    ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
+    EXPECT_EQ(sb.st_size, testfile_size);
+}
+
+TEST_F(FiemapWriterTest, CheckFileExtents) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+    EXPECT_GT(fptr->extents().size(), 0);
+}
+
+TEST_F(FiemapWriterTest, CheckWriteError) {
+    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+    ASSERT_NE(fptr, nullptr);
+
+    // prepare buffer for writing the pattern - 0xa0
+    uint64_t blocksize = fptr->block_size();
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+    ASSERT_NE(buffer, nullptr);
+    memset(buffer.get(), 0xa0, blocksize);
+
+    uint8_t* p = static_cast<uint8_t*>(buffer.get());
+    for (off64_t off = 0; off < testfile_size; off += blocksize) {
+        ASSERT_TRUE(fptr->Write(off, p, blocksize));
+    }
+
+    EXPECT_TRUE(fptr->Flush());
+}
+
+class TestExistingFile : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        std::string exec_dir = ::android::base::GetExecutableDirectory();
+        std::string unaligned_file = exec_dir + "/testdata/unaligned_file";
+        std::string file_4k = exec_dir + "/testdata/file_4k";
+        std::string file_32k = exec_dir + "/testdata/file_32k";
+        fptr_unaligned = FiemapWriter::Open(unaligned_file, 4097, false);
+        fptr_4k = FiemapWriter::Open(file_4k, 4096, false);
+        fptr_32k = FiemapWriter::Open(file_32k, 32768, false);
+    }
+
+    FiemapUniquePtr fptr_unaligned;
+    FiemapUniquePtr fptr_4k;
+    FiemapUniquePtr fptr_32k;
+};
+
+TEST_F(TestExistingFile, ErrorChecks) {
+    EXPECT_EQ(fptr_unaligned, nullptr);
+    EXPECT_NE(fptr_4k, nullptr);
+    EXPECT_NE(fptr_32k, nullptr);
+
+    EXPECT_EQ(fptr_4k->size(), 4096);
+    EXPECT_EQ(fptr_32k->size(), 32768);
+    EXPECT_GT(fptr_4k->extents().size(), 0);
+    EXPECT_GT(fptr_32k->extents().size(), 0);
+}
+
+TEST_F(TestExistingFile, CheckWriteError) {
+    ASSERT_NE(fptr_4k, nullptr);
+    // prepare buffer for writing the pattern - 0xa0
+    uint64_t blocksize = fptr_4k->block_size();
+    auto buff_4k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+    ASSERT_NE(buff_4k, nullptr);
+    memset(buff_4k.get(), 0xa0, blocksize);
+
+    uint8_t* p = static_cast<uint8_t*>(buff_4k.get());
+    for (off64_t off = 0; off < 4096; off += blocksize) {
+        ASSERT_TRUE(fptr_4k->Write(off, p, blocksize));
+    }
+    EXPECT_TRUE(fptr_4k->Flush());
+
+    ASSERT_NE(fptr_32k, nullptr);
+    // prepare buffer for writing the pattern - 0xa0
+    blocksize = fptr_32k->block_size();
+    auto buff_32k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+    ASSERT_NE(buff_32k, nullptr);
+    memset(buff_32k.get(), 0xa0, blocksize);
+    p = static_cast<uint8_t*>(buff_32k.get());
+    for (off64_t off = 0; off < 4096; off += blocksize) {
+        ASSERT_TRUE(fptr_32k->Write(off, p, blocksize));
+    }
+    EXPECT_TRUE(fptr_32k->Flush());
+}
+
+class VerifyBlockWritesExt4 : public ::testing::Test {
+    // 2GB Filesystem and 4k block size by default
+    static constexpr uint64_t block_size = 4096;
+    static constexpr uint64_t fs_size = 2147483648;
+
+  protected:
+    void SetUp() override {
+        fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
+        uint64_t count = fs_size / block_size;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path.c_str(), block_size, count);
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
+        // create mount point
+        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+        // Get and attach a loop device to the filesystem we created
+        LoopDevice loop_dev(fs_path);
+        ASSERT_TRUE(loop_dev.valid());
+        // create file system
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // mount the file system
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
+    }
+
+    void TearDown() override {
+        umount(mntpoint.c_str());
+        rmdir(mntpoint.c_str());
+        unlink(fs_path.c_str());
+    }
+
+    std::string mntpoint;
+    std::string fs_path;
+};
+
+TEST_F(VerifyBlockWritesExt4, CheckWrites) {
+    EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
+
+    std::string file_path = mntpoint + "/testfile";
+    uint64_t file_size = 100 * 1024 * 1024;
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(buffer, nullptr);
+    memset(buffer.get(), 0xa0, getpagesize());
+    {
+        // scoped fiemap writer
+        FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
+        ASSERT_NE(fptr, nullptr);
+        uint8_t* p = static_cast<uint8_t*>(buffer.get());
+        for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+            ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
+        }
+        EXPECT_TRUE(fptr->Flush());
+    }
+    // unmount file system here to make sure we invalidated all page cache and
+    // remount the filesystem again for verification
+    ASSERT_EQ(umount(mntpoint.c_str()), 0);
+
+    LoopDevice loop_dev(fs_path);
+    ASSERT_TRUE(loop_dev.valid());
+    ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0)
+            << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
+            << strerror(errno);
+
+    ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
+    ASSERT_NE(fd, -1);
+    auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(filebuf, nullptr);
+    for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+        memset(filebuf.get(), 0x00, getpagesize());
+        ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
+        ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
+                << "Invalid pattern at offset: " << off << " size " << getpagesize();
+    }
+}
+
+class VerifyBlockWritesF2fs : public ::testing::Test {
+    // 2GB Filesystem and 4k block size by default
+    static constexpr uint64_t block_size = 4096;
+    static constexpr uint64_t fs_size = 2147483648;
+
+  protected:
+    void SetUp() override {
+        fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
+        uint64_t count = fs_size / block_size;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path.c_str(), block_size, count);
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
+        // create mount point
+        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+        // Get and attach a loop device to the filesystem we created
+        LoopDevice loop_dev(fs_path);
+        ASSERT_TRUE(loop_dev.valid());
+        // create file system
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // mount the file system
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
+    }
+
+    void TearDown() override {
+        umount(mntpoint.c_str());
+        rmdir(mntpoint.c_str());
+        unlink(fs_path.c_str());
+    }
+
+    std::string mntpoint;
+    std::string fs_path;
+};
+
+TEST_F(VerifyBlockWritesF2fs, CheckWrites) {
+    EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
+
+    std::string file_path = mntpoint + "/testfile";
+    uint64_t file_size = 100 * 1024 * 1024;
+    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(buffer, nullptr);
+    memset(buffer.get(), 0xa0, getpagesize());
+    {
+        // scoped fiemap writer
+        FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
+        ASSERT_NE(fptr, nullptr);
+        uint8_t* p = static_cast<uint8_t*>(buffer.get());
+        for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+            ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
+        }
+        EXPECT_TRUE(fptr->Flush());
+    }
+    // unmount file system here to make sure we invalidated all page cache and
+    // remount the filesystem again for verification
+    ASSERT_EQ(umount(mntpoint.c_str()), 0);
+
+    LoopDevice loop_dev(fs_path);
+    ASSERT_TRUE(loop_dev.valid());
+    ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0)
+            << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
+            << strerror(errno);
+
+    ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
+    ASSERT_NE(fd, -1);
+    auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+    ASSERT_NE(filebuf, nullptr);
+    for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+        memset(filebuf.get(), 0x00, getpagesize());
+        ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
+        ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
+                << "Invalid pattern at offset: " << off << " size " << getpagesize();
+    }
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    if (argc <= 1) {
+        cerr << "Filepath with its bdev path must be provided as follows:" << endl;
+        cerr << "  $ fiemap_writer_test </dev/block/XXXX" << endl;
+        cerr << "  where, /dev/block/XXX is the block device where the file resides" << endl;
+        exit(EXIT_FAILURE);
+    }
+    ::android::base::InitLogging(argv, ::android::base::StderrLogger);
+
+    testbdev = argv[1];
+    if (argc > 2) {
+        testfile_size = strtoull(argv[2], NULL, 0);
+        if (testfile_size == ULLONG_MAX) {
+            testfile_size = 512 * 1024 * 1024;
+        }
+    }
+
+    return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
new file mode 100644
index 0000000..ae61344
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <linux/fiemap.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fiemap_writer {
+
+class FiemapWriter;
+using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
+
+class FiemapWriter final {
+  public:
+    // Factory method for FiemapWriter.
+    // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
+    // to the given file directly using raw block i/o.
+    static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
+                                bool create = true);
+
+    // Syncs block device writes.
+    bool Flush() const;
+
+    // Writes the file by using its FIEMAP and performing i/o on the raw block device.
+    // The return value is success / failure. This will happen in particular if the
+    // kernel write returns errors, extents are not writeable or more importantly, if the 'size' is
+    // not aligned to the block device's block size.
+    bool Write(off64_t off, uint8_t* buffer, uint64_t size);
+
+    // The counter part of Write(). It is an error for the offset to be unaligned with
+    // the block device's block size.
+    // In case of error, the contents of buffer MUST be discarded.
+    bool Read(off64_t off, uint8_t* buffer, uint64_t size);
+
+    ~FiemapWriter() = default;
+
+    const std::string& file_path() const { return file_path_; };
+    uint64_t size() const { return file_size_; };
+    const std::string& bdev_path() const { return bdev_path_; };
+    uint64_t block_size() const { return block_size_; };
+    const std::vector<struct fiemap_extent>& extents() { return extents_; };
+
+    // Non-copyable & Non-movable
+    FiemapWriter(const FiemapWriter&) = delete;
+    FiemapWriter& operator=(const FiemapWriter&) = delete;
+    FiemapWriter& operator=(FiemapWriter&&) = delete;
+    FiemapWriter(FiemapWriter&&) = delete;
+
+  private:
+    // Name of the file managed by this class.
+    std::string file_path_;
+    // Block device on which we have created the file.
+    std::string bdev_path_;
+
+    // File descriptors for the file and block device
+    ::android::base::unique_fd file_fd_;
+    ::android::base::unique_fd bdev_fd_;
+
+    // Size in bytes of the file this class is writing
+    uint64_t file_size_;
+
+    // total size in bytes of the block device
+    uint64_t bdev_size_;
+
+    // Filesystem type where the file is being created.
+    // See: <uapi/linux/magic.h> for filesystem magic numbers
+    uint32_t fs_type_;
+
+    // block size as reported by the kernel of the underlying block device;
+    uint64_t block_size_;
+
+    // This file's fiemap
+    std::vector<struct fiemap_extent> extents_;
+
+    FiemapWriter() = default;
+
+    uint64_t WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer, off64_t logical_off,
+                         uint64_t length);
+};
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/testdata/file_32k b/fs_mgr/libfiemap_writer/testdata/file_32k
new file mode 100644
index 0000000..12f3be4
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_32k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/file_4k b/fs_mgr/libfiemap_writer/testdata/file_4k
new file mode 100644
index 0000000..08e7df1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_4k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/unaligned_file b/fs_mgr/libfiemap_writer/testdata/unaligned_file
new file mode 100644
index 0000000..c107c26
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/unaligned_file
Binary files differ
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index f56a517..c985a97 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -170,16 +170,32 @@
 
 AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
                                                    AvbSlotVerifyFlags flags,
-                                                   AvbSlotVerifyData** out_data) {
+                                                   std::vector<VBMetaData>* out_vbmeta_images) {
     // Invokes avb_slot_verify() to load and verify all vbmeta images.
     // Sets requested_partitions to nullptr as it's to copy the contents
     // of HASH partitions into handle>avb_slot_data_, which is not required as
     // fs_mgr only deals with HASHTREE partitions.
     const char* requested_partitions[] = {nullptr};
+
+    // Local resource to store vbmeta images from avb_slot_verify();
+    AvbSlotVerifyData* avb_slot_data;
+
     // The |hashtree_error_mode| field doesn't matter as it only
     // influences the generated kernel cmdline parameters.
-    return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
-                           AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
+    auto verify_result =
+            avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
+                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &avb_slot_data);
+
+    // Copies avb_slot_data->vbmeta_images[].
+    for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
+        out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,
+                                                   avb_slot_data->vbmeta_images[i].vbmeta_size));
+    }
+
+    // Free the local resource.
+    avb_slot_verify_data_free(avb_slot_data);
+
+    return verify_result;
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index e6b33c2..c0f12aa 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -25,7 +25,9 @@
 #pragma once
 
 #include <string>
+#include <vector>
 
+#include <fs_avb/fs_avb.h>
 #include <libavb/libavb.h>
 
 namespace android {
@@ -55,7 +57,7 @@
                                   void* buffer, size_t* out_num_read);
 
     AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
-                                      AvbSlotVerifyData** out_data);
+                                      std::vector<VBMetaData>* out_vbmeta_images);
 
   private:
     AvbOps avb_ops_;
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 89c755e..cf920f9 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -99,14 +99,13 @@
 }
 
 template <typename Hasher>
-static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
+static std::pair<size_t, bool> verify_vbmeta_digest(const std::vector<VBMetaData>& vbmeta_images,
                                                     const uint8_t* expected_digest) {
     size_t total_size = 0;
     Hasher hasher;
-    for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
-        hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
-                      verify_data.vbmeta_images[n].vbmeta_size);
-        total_size += verify_data.vbmeta_images[n].vbmeta_size;
+    for (size_t n = 0; n < vbmeta_images.size(); n++) {
+        hasher.update(vbmeta_images[n].vbmeta_data(), vbmeta_images[n].vbmeta_size());
+        total_size += vbmeta_images[n].vbmeta_size();
     }
 
     bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
@@ -123,7 +122,7 @@
   public:
     // The factory method to return a unique_ptr<AvbVerifier>
     static std::unique_ptr<AvbVerifier> Create();
-    bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
+    bool VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images);
 
   protected:
     AvbVerifier() = default;
@@ -186,8 +185,8 @@
     return avb_verifier;
 }
 
-bool AvbVerifier::VerifyVbmetaImages(const AvbSlotVerifyData& verify_data) {
-    if (verify_data.num_vbmeta_images == 0) {
+bool AvbVerifier::VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images) {
+    if (vbmeta_images.empty()) {
         LERROR << "No vbmeta images";
         return false;
     }
@@ -197,10 +196,10 @@
 
     if (hash_alg_ == kSHA256) {
         std::tie(total_size, digest_matched) =
-                verify_vbmeta_digest<SHA256Hasher>(verify_data, digest_);
+                verify_vbmeta_digest<SHA256Hasher>(vbmeta_images, digest_);
     } else if (hash_alg_ == kSHA512) {
         std::tie(total_size, digest_matched) =
-                verify_vbmeta_digest<SHA512Hasher>(verify_data, digest_);
+                verify_vbmeta_digest<SHA512Hasher>(vbmeta_images, digest_);
     }
 
     if (total_size != vbmeta_size_) {
@@ -306,18 +305,18 @@
 }
 
 static bool get_hashtree_descriptor(const std::string& partition_name,
-                                    const AvbSlotVerifyData& verify_data,
+                                    const std::vector<VBMetaData>& vbmeta_images,
                                     AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
                                     std::string* out_digest) {
     bool found = false;
     const uint8_t* desc_partition_name;
 
-    for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) {
+    for (size_t i = 0; i < vbmeta_images.size() && !found; i++) {
         // Get descriptors from vbmeta_images[i].
         size_t num_descriptors;
         std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
-                avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
-                                       verify_data.vbmeta_images[i].vbmeta_size, &num_descriptors),
+                avb_descriptor_get_all(vbmeta_images[i].vbmeta_data(),
+                                       vbmeta_images[i].vbmeta_size(), &num_descriptors),
                 avb_free);
 
         if (!descriptors || num_descriptors < 1) {
@@ -377,7 +376,7 @@
     AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
                                                   : AVB_SLOT_VERIFY_FLAGS_NONE;
     AvbSlotVerifyResult verify_result =
-            avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
+            avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
 
     // Only allow two verify results:
     //   - AVB_SLOT_VERIFY_RESULT_OK.
@@ -417,8 +416,7 @@
     //     and AVB HASHTREE descriptor(s).
     AvbVBMetaImageHeader vbmeta_header;
     avb_vbmeta_image_header_to_host_byte_order(
-            (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
-            &vbmeta_header);
+            (AvbVBMetaImageHeader*)avb_handle->vbmeta_images_[0].vbmeta_data(), &vbmeta_header);
     bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
                                   AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
 
@@ -431,7 +429,7 @@
             LERROR << "Failed to create AvbVerifier";
             return nullptr;
         }
-        if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
+        if (!avb_verifier->VerifyVbmetaImages(avb_handle->vbmeta_images_)) {
             LERROR << "VerifyVbmetaImages failed";
             return nullptr;
         }
@@ -449,8 +447,7 @@
 }
 
 AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
-    if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
-        avb_slot_data_->num_vbmeta_images < 1) {
+    if (!fstab_entry || status_ == kAvbHandleUninitialized || vbmeta_images_.size() < 1) {
         return AvbHashtreeResult::kFail;
     }
 
@@ -478,7 +475,7 @@
     AvbHashtreeDescriptor hashtree_descriptor;
     std::string salt;
     std::string root_digest;
-    if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
+    if (!get_hashtree_descriptor(partition_name, vbmeta_images_, &hashtree_descriptor, &salt,
                                  &root_digest)) {
         return AvbHashtreeResult::kFail;
     }
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 08bdbdc..0c2b231 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -18,6 +18,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <fstab/fstab.h>
 #include <libavb/libavb.h>
@@ -31,6 +32,35 @@
     kDisabled,
 };
 
+class VBMetaData {
+  public:
+    // Constructors
+    VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
+
+    VBMetaData(uint8_t* data, size_t size)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]), vbmeta_size_(size) {
+        // The ownership of data is NOT transferred, i.e., the caller still
+        // needs to release the memory as we make a copy here.
+        memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
+    }
+
+    explicit VBMetaData(size_t size)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]), vbmeta_size_(size) {}
+
+    // Get methods for each data member.
+    const std::string& device_path() const { return device_path_; }
+    uint8_t* vbmeta_data() const { return vbmeta_ptr_.get(); }
+    const size_t& vbmeta_size() const { return vbmeta_size_; }
+
+    // Maximum size of a vbmeta data - 64 KiB.
+    static const size_t kMaxVBMetaSize = 64 * 1024;
+
+  private:
+    std::string device_path_;
+    std::unique_ptr<uint8_t[]> vbmeta_ptr_;
+    size_t vbmeta_size_;
+};
+
 class FsManagerAvbOps;
 
 class AvbHandle;
@@ -43,7 +73,7 @@
   public:
     // The factory method to return a AvbUniquePtr that holds
     // the verified AVB (external/avb) metadata of all verified partitions
-    // in avb_slot_data_.vbmeta_images[].
+    // in vbmeta_images_.
     //
     // The metadata is checked against the following values from /proc/cmdline.
     //   - androidboot.vbmeta.{hash_alg, size, digest}.
@@ -95,12 +125,6 @@
     AvbHandle(AvbHandle&&) noexcept = delete;             // no move
     AvbHandle& operator=(AvbHandle&&) noexcept = delete;  // no move assignment
 
-    ~AvbHandle() {
-        if (avb_slot_data_) {
-            avb_slot_verify_data_free(avb_slot_data_);
-        }
-    };
-
   private:
     enum AvbHandleStatus {
         kAvbHandleSuccess = 0,
@@ -110,9 +134,9 @@
         kAvbHandleVerificationError,
     };
 
-    AvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
+    AvbHandle() : status_(kAvbHandleUninitialized) {}
 
-    AvbSlotVerifyData* avb_slot_data_;
+    std::vector<VBMetaData> vbmeta_images_;
     AvbHandleStatus status_;
     std::string avb_version_;
 };
diff --git a/fs_mgr/libfs_avb/tests/Android.bp b/fs_mgr/libfs_avb/tests/Android.bp
new file mode 100644
index 0000000..24e1d76
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2019 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_test_host {
+    name: "libfs_avb_host_unittest",
+    required: [
+        "avbtool",
+    ],
+    data: [
+        "data/*",
+    ],
+    static_libs: [
+        "libgtest_host",
+    ],
+    shared_libs: [
+        "libbase",
+        "libchrome",
+    ],
+    srcs: [
+        "fs_avb_unittest_util.cpp",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem
new file mode 100644
index 0000000..867dcff
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
+4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
+gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
+DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
+uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
+YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
+SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
+jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
+z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
+mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
+o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
+zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
+5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
+BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
+vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
+i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
+iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
+mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
+b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
+oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
+lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
+nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
+PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
+vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
+GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem
new file mode 100644
index 0000000..26db5c3
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEA2ASv49OEbH4NiT3CjNMSVeliyfEPXswWcqtEfCxlSpS1FisA
+uwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3OhiuVKgV/rCtrDXaO60nvK/o0y83
+NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0Grjnx/r5CXerl5PrRK7PILzwgBHb
+IwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw7W6LvjBb9qav3YB8RV6PkZNeRP64
+ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWjXsrcVy8+8Mldhmr4r2an7c247aFf
+upuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH59gJjKhot0RpmGxZBvb33TcBK5SdJ
+X39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnYeUX/A0wmogBajsJRoRX5e/RcgZsY
+RzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6sklFL0fHDUE/l4BNP8G1u3Bfpzev
+SCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3BwFeq+xmwfYrP0LRaH+1YeRauuMuRe
+ke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfXDWxJx/XEkjGLCe4z2qk3tkkY+A5g
+Rcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwAp13MfC7FlYujO/BDLl7dANsCAwEA
+AQKCAgAWoL8P/WsktjuSwb5sY/vKtgzcHH1Ar942GsysuTXPDy686LpF3R8T/jNy
+n7k2UBAia8xSoWCR6BbRuHeV5oA+PLGeOpE7QaSfonB+yc+cy0x3Or3ssfqEsu/q
+toGHp75/8DXS6WE0K04x94u1rdC9b9sPrrGBlWCLGzqM0kbuJfyHXdd3n2SofAUO
+b5QRSgxD+2tHUpEroHqHnWJCaf4J0QegX45yktlfOYNK/PHLDQXV8ly/ejc32M4Y
+Tv7hUtOOJTuq8VCg9OWZm2Zo1QuM9XEJTPCp5l3+o5vzO6yhk2gotDvD32CdA+3k
+tLJRP54M1Sn+IXb1gGKN9rKAtGJbenWIPlNObhQgkbwG89Qd+5rfMXsiPv1Hl1tK
++tqwjD82/H3/ElaaMnwHCpeoGSp95OblAoBjzjMP2KsbvKSdL8O/rf1c3uOw9+DF
+cth0SA8y3ZzI11gJtb2QMGUrCny5n4sPGGbc3x38NdLhwbkPKZy60OiT4g2kNpdY
+dIitmAML2otttiF4AJM6AraPk8YVzkPLTksoL3azPBya5lIoDI2H3QvTtSvpXkXP
+yKchsDSWYbdqfplqC/X0Djp2/Zd8jpN5I6+1aSmpTmbwx/JTllY1N89FRZLIdxoh
+2k81LPiXhE6uRbjioJUlbnEWIpY2y2N2Clmxpjh0/IcXd1XImQKCAQEA7Zai+yjj
+8xit24aO9Tf3mZBXBjSaDodjC2KS1yCcAIXp6S7aH0wZipyZpQjys3zaBQyMRYFG
+bQqIfVAa6inWyDoofbAJHMu5BVcHFBPZvSS5YhDjc8XZ5dqSCxzIz9opIqAbm+b4
+aEV/3A3Jki5Dy8y/5j21GAK4Y4mqQOYzne7bDGi3Hyu041MGM4qfIcIkS5N1eHW4
+sDZJh6+K5tuxN5TX3nDZSpm9luNH8mLGgKAZ15b1LqXAtM5ycoBY9Hv082suPPom
+O+r0ybdRX6nDSH8+11y2KiP2kdVIUHCGkwlqgrux5YZyjCZPwOvEPhzSoOS+vBiF
+UVXA8idnxNLk1QKCAQEA6MIihDSXx+350fWqhQ/3Qc6gA/t2C15JwJ9+uFWA+gjd
+c/hn5HcmnmBJN4R04nLG/aU9SQur87a4mnC/Mp9JIARjHlZ/WNT4U0sJyPEVRg5U
+Z9VajAucWwi0JyJYCO1EMMy68Jp8qlTriK/L7nbD86JJ5ASxjojiN/0psK/Pk60F
+Rr+shKPi3jRQ1BDjDtAxOfo4ctf/nFbUM4bY0FNPQMP7WesoSKU0NBCRR6d0d2tq
+YflMjIQHx+N74P5jEdSCHTVGQm+dj47pUt3lLPLWc0bX1G/GekwXP4NUsR/70Hsi
+bwxkNnK2TSGzkt2rcOnutP125rJu6WpV7SNrq9rm7wKCAQAfMROcnbWviKHqnDPQ
+hdR/2K9UJTvEhInASOS2UZWpi+s1rez9BuSjigOx4wbaAZ4t44PW7C3uyt84dHfU
+HkIQb3I5bg8ENMrJpK9NN33ykwuzkDwMSwFcZ+Gci97hSubzoMl/IkeiiN1MapL4
+GhLUgsD+3UMVL+Y9SymK8637IgyoCGdiND6/SXsa8SwLJo3VTjqx4eKpX7cvlSBL
+RrRxc50TmwUsAhsd4CDl9YnSATLjVvJBeYlfM2tbFPaYwl1aR8v+PWkfnK0efm60
+fHki33HEnGteBPKuGq4vwVYpn6bYGwQz+f6335/A2DMfZHFSpjVURHPcRcHbCMla
+0cUxAoIBAQC25eYNkO478mo+bBbEXJlkoqLmvjAyGrNFo48F9lpVH6Y0vNuWkXJN
+PUgLUhAu6RYotjGENqG17rz8zt/PPY9Ok2P3sOx8t00y1mIn/hlDZXs55FM0fOMu
+PZaiscAPs7HDzvyOmDah+fzi+ZD8H2M3DS2W+YE0iaeJa2vZJS2t02W0BGXiDI33
+IZDqMyLYvwwPjOnShJydEzXID4xLl0tNjzLxo3GSNA7jYqlmbtV8CXIc7rMSL6WV
+ktIDKKJcnmpn3TcKeX6MEjaSIT82pNOS3fY3PmXuL+CMzfw8+u77Eecq78fHaTiL
+P5JGM93F6mzi19EY0tmInUBMCWtQLcENAoIBAQCg0KaOkb8T36qzPrtgbfou0E2D
+ufdpL1ugmD4edOFKQB5fDFQhLnSEVSJq3KUg4kWsXapQdsBd6kLdxS+K6MQrLBzr
+4tf0c7UCF1AzWk6wXMExZ8mRb2RkGZYQB2DdyhFB3TPmnq9CW8JCq+6kxg/wkU4s
+vM4JXzgcqVoSf42QJl+B9waeWhg0BTWx01lal4ds88HvEKmE0ik5GwiDbr7EvDDw
+E6UbZtQcIoSTIIZDgYqVFfR2DAho3wXJRsOXh433lEJ8X7cCDzrngFbQnlKrpwML
+Xgm0SIUc+Nf5poMM3rfLFK77t/ob4w+5PwRKcoSniyAxrHd6bwykYA8Vuydv
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem
new file mode 100644
index 0000000..a383428
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem
@@ -0,0 +1,99 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIISKgIBAAKCBAEA0D3T+dISsmCHm797wsX0vVfqUWDJ/3mvDYozlCabDhnGLlSE
+pAQbf1Z8Ts+OM4pVRHOJUJL0WebNdmPPGjsyWQz6zZE96lQZL3avCEXqYVQR66V5
+3wdK/ohaMSRnGyEMBrqkVVbF3gCr+/irxD3YK+VowO2WKs/6GrMdqTA8Y5CTF/Je
+ptwsSg5MMjr6UaK4qDcrej3hkgBVGvRV3cj1snK6Br8HuYdFnpGGTS0d7UJlHFgl
+trGHU/CBO923hkHgJaWEjC0giSGjhKKtLzrVcpDV2y/lWQP9T/T4djEAIaHqQ++P
+SdOSR6psIGR6hVgSigt7HCnE7nW711/rfV5Ur9EiVpB040mDImKZcy8//TMnXydN
+1KYTVd/34fdpzMpSw5iblErbwOLXVTUmOztYnpl41feHSv/jPesHstPlfklIF2vo
+GZEohf9scQvcuM7wEBfC/aTA9K39zMmkBbcvSZjLyhmcSZWMPPOZyIcl3zY53QhW
+QC/abmIcBfI1S4+r7mC4i2Jn++oEvuGNVGr2SY2Z0ZZxXGL1HI/08D/3+Tcumrcn
+4YjPK/DMFi0F+e+1x41lipuf+cx/2qRNQX/m02STrLYdM6e0g33KvlnFdi2b752y
+/OIaMwxDaJvunMh6EMDWKM1AHbY/ioAoK7eS26HeJLEDllqO4+SWP37c8lMvSEWy
+1GiErR0HcsOj/QwWGPFseoVroMiA2sUQ0Ic/tgVjCTlXg+12XpUnouIweCi8KcL/
+ad2zJkju9hBhJLBQ/2GnivJi3lFgF4Gd//TSJ6rgWuXFfMKt/9z2Sz35ohEX4yA0
+flqlCeLInFEoevbz+XT9aRfDe65MZ79yw3TfP9CrV74hf1RRzveD4zpi3F+hcY2i
+JWsH7gROZeCm6fAX5Trecd3hOxJOfA4N4rvSSCq6BwCvebT8FY25Z/VF7cQrHYDS
+ij5w6lqhMzXHeUEY90Ga9AK4XzaWwGgezq+R7Zs00YSKqFv9qYNKdR7tz3cjijWf
+9q/3R1uh6EQKTMZKo4SEClJiGyjOBvmPK09jMFZTJv00hDxagDPZBl7XpLDJ5/Ln
+1uppvLCNWWY1zeJfaElMyq3/PqKZLidF9rVoA1SIwk2lpdUvPote2oFiwCZoXlwZ
+J2ncjmXgQNs76/8unDJA0rj4JPqccw4M5GxQ7okbgm3F4rmzriCuv8BeMSCkr2ry
+0mY3UhpohX4wCMq0G4x5sEUAz9FVVPZKjxnYBmLDzrJAR+4+G7gZsct01XDJYgDd
+JVYInFP22/cIre8VrFWYtHbgOFdNqUiVq58de6PdZG/E+uaWmEThSlRrgEjTxupi
+OXfgdKW/20j1qAtjOlqFwsY094Q5rqULQ6wPxQIDAQABAoIEAQChmkmlhrRBv42d
+fYUiyxK52b8ath0saJdDz6tlXmxYDgJxM9/XlORt9oTzeDknoEO5olu+rrx4BBgQ
+tzYiaiwRVXRREVTWQ7tjzRvaNL/GFkLt93XTccpuKwyrNE/bitLVagRbwcI+HZFa
+MknCOihHMHoRto8h3FKAY94xzSAgODMek1WG8jhgpCXXmVNnBPt+d4oDDIDAGAfz
+qgf03J5nhIb+80KgZOzPOKnbvJaL6EmlLHbgB3c42dzAw7hHtVmofYGWcvLb2MIY
+DVKO435/sQx1U/8NDH6JjVdACZjLgObXH9K3/Tt46DWPEcrPLmD8xhoc6gFM+Qr0
+AhkzKoBYDNk0CljbhdIBXjktXU6wRQFZ45uP2e4JZ4zrzGBLr/t4lTavZ0SQtLld
+A6kOsGh+dCWFDtnshxYnl/xad/yR+3a5zmDJbo/fJTBXrlf1B4rfQkFtK20etOPQ
+B++FC/rjh3Mm/Kb/p9Gz/2upZdArH97ZvD2LBFfj77lFmAhqAi3wCRlN+ekuYxaZ
+t1pBV9yXig8Dyldg1d7X8pOn2kyrF3rQUDDf4pa7x9vpnbkUlEUifoV9gnYsmdni
+qDzYBtTv2g6MKqwQySXaIUW0YOBPbOellWEwxJqGYQ7y4IfVHfM0iyHnehk2tZcr
++XazLnwGe+Bz4vcguFhJXLyIu//lAOhZtbk6r1QJEUuxaOOQX3wzyceE6nkDsgmr
+P5dj3Zpd7fS2VV2vyGHIFnBJ88LRxreVvgr6Q28UT27SB82zMb7mRZTVE2zeuubT
+5D2D1XbZ0wBo6WiK6eRRrDQ2Haeetkj/uoRy6PWXwnAaTmmIrrXwLqaoJh/U1e+D
+tfsDLWd6IxLjfXvGglrHsrtAz0oprpixUTeVhgTrGk9IQRd5rvxuGUYhFujVaYI6
++QUf+33AFdtncb8y9C9jZmgx8AKbJk+e73SLhB5JVos+WteU7b8d/Mim5mALjnO6
+Z1n/uimsT79sSDqy3XSymtKWXo/22UlrvGCpoEuELPMb6dSFWR7vwrsvhFngY4/K
+UnitnvxboEflQnaIQ4IfRLRzZsX+sC5Esqw9U5tHt4oI+91Dv3KbdbcERgV73K6B
+ZQgC4lkAQquFXiZ5AICkxjiMyZwTtU9KJ7xv17Xu6oywF/3AtbVGETW1D+3maHsD
+y3DASWojyqZdLj+WGzKQRa+swgCDAYKeek2fIAXFSdF63zxJ2RxOJ4GijSaoh+mr
+4HVvcpDaTj+A8T1+QdByM4s98gu4GD7kVtVQGBZdWjutyHvh0hWv1gtVmbhQ/413
+gDMFFDzHIjLTYGYes4hHL22169jVR9sZ1eQxwvTIg3N4pD5cFm0rRuZZTS+oJToF
+G27aBFihAoICAQDyVB62ZDnbxQthk+zITKIzRUrJbLoXrUcANcSHfaN7inF87Ova
+ze7ejT9DNSEhbtfZFJ1G6diOYoSw+2MzFXv0gEkLKY0dETydKgHEu6nVq5eivMgv
+D4hc9YkJMHDSlmv2FDkpL3AXCAmnW9rKp+ddttBZECnmlPEpHLoj6xgBw3pNa1Xs
+IcLVfdugH86Hexj6o0oKgYfcqrX8UUHtUI2/XQqgFrIj8ksjf1fFVWJRJFWmBXqp
+nMEsYarzATeM1kQ/kDeT1ZUpoGPQt02/XqXT4B5A3ATiEtpM2u+l48xtogWWg2Ry
+G9l938StAmhUiW1m7GnKE6EIFvQY85WvbzxOR0JYVUSr7MrasF6nnQlhYxFuIJoJ
+2h/KJQao5GCTvG4+GtbJJm4c2nyZgwyhizMsdgsdcls79aXiMkrZZkamLVUZWOtE
+3pA/oBuz2qnO9HwjbH1HGOccq0TXfmpFScEV3CQGYJdno6Fy7cbmupaL4U9agQ4e
+w+ygL18nq5HV++LStFnVrgs5YijjskfRdE9GUMVDh5pCsd9Y23Fymaad4O/2SRCC
+YkSsyH5OvyDOLpoyUJ6g6Q+45Hqm/3lG4YjNpzFUiMcnp7+3xU35qC0LK8xEfeei
+Ms1mTVEiHNIp6xH/TqRdX73WD7+YuKZSLIfRG7dgrirU6w+mhhvxD51uHQKCAgEA
+2/1mBCR5qm3/0Lt++RQbeyE3tiw40UeyQqucG/+VvY77sSLkI/Lx8iwRlywXcLBn
++A4TvgukmAdWzCs8ndgKNxPA+gfohvBsMOGN9KOB1Ug5vvg2J2kiI64vwYCwzhdZ
+NTUUmL+GMFHUqSsWYg6i7iBFcZmznr4W2T3bBxyTMZki7JStB86e35KXrzc2/W/b
++/p5U2HCSazDHI5mMyuClHc6GmUSVJ7f7LHjL94jviNqobp0Vj603tScHISmNrZw
+TBavkvZGYXsoWKvqavk7jBB9QzaBL+unaFRslg5jTaiKnISj44Us1fjFKu84xifL
+nJaEzjDPt7PBxko7LPgEY7wF39nM9VpoetI7bwR6NwDLSX8UU97MGd+HY+MO1Wi1
+pd2Lapwrx/EK7Oxz335VRK4Je0aZna4j2TyQdMJac9fsGPXv4ZsLfDLj/wD6l1j+
+lLLbBv3ImdSj32LBbhsgF4iCGeXO8HpPO+Q/h9XVsnY52Um2XdNMn03PCGm6ZvtM
+7DXiS+lPF90HjolJVHZTBNtdVRrLr53zLuWEfqT4FeKrDaxdtiXkxLjrB+5/VYu7
+ntyk01ZQ63VNfEwS1irmKl9+qZkTHk3HHV9jNV5RzWViwmJI7Wpr1YzBwmcKCB1O
+oGUADDs8QpnkCz0xkMVtYwHj9qKZlqfbHzrFDUUcF8kCggIAdYvUcgjf//ju8mA8
+5VQ3AcPE6TvycPW+kR2DvW12VcDsF/sc1UA7dHzziPhGn98SmNxlBjb8suSbFPZ8
+QhVT0WBBDkcTilwIGPx9ax7U3S6lGW2VdS6FqQH5fRmgQKZyrCVXLOEz8BgYBrSJ
+xu/3TQAWxH0QtibdbGHg8Pdi58gYlWFRhn9B8Slh1aRYHGPb1AhNLBd0/ddY+5G2
+9xSyDXdmZg1cUA+B3zAwNSqbzFxhp2zU+V1uXsbpk4KtnYV6CZM9QlrCRjTk9iNU
+dVXF/qaiRjfzrm4SsmEpCkEbsrp7F22Y1bkooORglMOsNAWNqfVXw4wN+syXj1ro
+6vZ8PERYrFyAOR1dsQMIhymnmTPjCpaJ4emKrhWTy20sY71thHakZWJc22YoNpbZ
+E6tgIVsJPTlxg/4+fyCCKj5wWr92nhsB1KBZPGO/zFhvMlJpvQ0tH8W2pbN2a0mI
+5x9FqALm/qjwCHfZItSwPM+ZozSht3cOkGHdcD5KXAXfcfsDJc4SHZKVIzq4NusN
+504R/jvD1GP8sglyG7omp75ckgzAmakLdxOP2HhQvIX9tcXpSirNJ6Sl2bwKuuMF
+wxo3r/o/9Y97e4LlfpEYp9eqMdcG+NpR993IwK0UhAWS9H5wdnWBSUHd5e4xtDUt
+iILNRuO46g7R/AIhz1cSSraWWQkCggIBAMhhPP5C9yt9PIm1b0eTwCBctnFSQIKo
+KsA9rll2ab+bMLk9jc8M6MLszy0CtWso09sHf4YY9tifvrkEHRethEh8zscwUuYu
+sm2n1fTixk0ul6LSVgl54uXbMJayENn4PIKRkew8cA8tSma43497w37hmD+MgCb1
+ALzqcco9hfmkgkI6fo1g8Ce3UEECKy2YKSmREdgYcK9JFQO61W6AkFWJcDxAmfzI
+JjFkKwsb7TSw79zWiEdSoM9jm7sCPKATd6Bm/ZAAkUUTuEFkfobn9Ax1rJN/Xxb2
+MKuAUtQv0NYY0gEVdG62jItuKLId6nncH8PG+rsRjPLIYpWqYdJpKx5pUnR+4AkQ
+S6CsRASwcF4PdBvDDBIFG6XpjFo4pPdQhDzL2sTF8b8SWSBLlJQbb7G6UNqgCSau
+SusCFpazvU5NfDmUMuctob2EYVaSXq9jGaj6bTUmDwXHwWilfIk9XfLxnYfXYrJ6
+xhdIpXGmHhuLQtAgK2O1JtLoPc9s9qP8/SkfP7xjjG6xHsP/WvL7QE1pPs9ZM/UI
+C01JNHFi9LKCn8o5mbZjN8jUowi7ffK+76wZUG1L7zM5ytWQOYwo0TQBfc8fpmFw
++RBRJX2kJyDO27ExczoGOKjwqEDaODIB9+9zcCK0BgSoRibSm4ZBvoxzWWD65Kls
+xdPhZUHcFGW5AoICAQC8iG27aD8aRUt94Oek66gFOJx84QVZehWPqtZjWyVenDuc
+T8dink8oejGjcK2UJuQDa83azv90ocVqE0n0ronYyszt9Ib1jlYC+CK1Ar9TYGFg
+WU5OWEDyCzCpqW/w/aG68U8qhKm0MvkLJR+G6evan9TwEhFEVAm3iWllNXs9x29s
+BucwyMMC23zsimxYlS7dA4DtyvVA+zL1omLpSWHbU/qtuI3HV1NeJzsy+gC4mwPh
+j52tdl669fyWLzHzBRLeq6dVOedjnCo+jlU3dL20DEk9SaW08D1CPuZekV1jVPMw
+JoaDcIRh4KLtQ0BYZ7UJeFUTsx1CS/+UqzqYSPOi57a5kvr0Y8YwRnSB8dHVFttX
+JTv83wTQXHPFSBgfnHNe7lsRTfIQfuIkr2bpiU7h85UQ7LsqcI6YHaC07URcsGFF
+FrLWGh91qzAd1diSHla2RnY3n8PPuMnCkguNhLUrYdmyMol7FfWFa9lwplsuTzBq
+B6yj8iaiE3LL+Q/eulJ7S6QPfAI2bU0UJO23Y4koeoIibEEDMSCQ6KYZ2NClRRRT
+ga5fS1YfkDFEcHUQ1/KIkdYHGBKBjoKGExzi8+CgiSySVSYDZl6wIOhLjH2OZ3ol
+ldPN7iNAHirrxg9v8QO6OQlpLUk5Lhp/1dSlZ6sy3UjFqvax3tw6ZjrL88YP5g==
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.cpp
new file mode 100644
index 0000000..216d1cb
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2019 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 "fs_avb_unittest_util.h"
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+
+namespace fs_avb_host_test {
+
+void BaseFsAvbTest::SetUp() {
+    // Changes current directory to test executable directory so that relative path
+    // references to test dependencies don't rely on being manually run from
+    // the executable directory. With this, we can just open "./data/testkey_rsa2048.pem"
+    // from the source.
+    base::SetCurrentDirectory(base::FilePath(android::base::GetExecutableDirectory()));
+
+    // Creates a temporary directory, e.g., /tmp/libfs_avb-tests.XXXXXX to stash images in.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+    base::CreateTemporaryDirInDir(tmp_dir, "libfs_avb-tests.", &test_dir_);
+}
+
+void BaseFsAvbTest::TearDown() {
+    // Nukes temporary directory.
+    ASSERT_NE(std::string::npos, test_dir_.value().find("libfs_avb-tests"));
+    ASSERT_TRUE(base::DeleteFile(test_dir_, true /* recursive */));
+}
+
+std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
+                                            const std::string& hash_algorithm) {
+    auto iter = vbmeta_images_.find(file_name);
+    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
+
+    // Gets the image path from iterator->second.path: VBMetaImage.path.
+    base::FilePath image_path = iter->second.path;
+    base::FilePath vbmeta_digest_path = test_dir_.Append("vbmeta_digest");
+    EXPECT_COMMAND(0,
+                   "avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
+                   " --output %s",
+                   image_path.value().c_str(), hash_algorithm.c_str(),
+                   vbmeta_digest_path.value().c_str());
+    // Reads the content of the output digest file.
+    std::string vbmeta_digest_data;
+    EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
+    // Returns the trimmed digest.
+    std::string trimmed_digest_data;
+    base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
+    return trimmed_digest_data;
+}
+
+void BaseFsAvbTest::GenerateVBMetaImage(
+        const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
+        const base::FilePath& key_path,
+        const std::vector<base::FilePath>& include_descriptor_image_paths,
+        const std::vector<ChainPartitionConfig>& chain_partitions,
+        const std::string& additional_options) {
+    // --algorithm and --key
+    std::string signing_options;
+    if (avb_algorithm == "") {
+        signing_options = " --algorithm NONE ";
+    } else {
+        signing_options =
+                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+    }
+    // --include_descriptors_from_image
+    std::string include_descriptor_options;
+    for (const auto& path : include_descriptor_image_paths) {
+        include_descriptor_options += " --include_descriptors_from_image " + path.value();
+    }
+    // --chain_partitions
+    std::string chain_partition_options;
+    for (const auto& partition : chain_partitions) {
+        chain_partition_options += base::StringPrintf(
+                " --chain_partition %s:%u:%s", partition.partition_name.c_str(),
+                partition.rollback_index_location, partition.key_blob_path.value().c_str());
+    }
+    // Starts to 'make_vbmeta_image'.
+    VBMetaImage vbmeta_image;
+    vbmeta_image.path = test_dir_.Append(file_name);
+    EXPECT_COMMAND(0,
+                   "avbtool make_vbmeta_image"
+                   " --rollback_index %" PRIu64
+                   " %s %s %s %s"
+                   " --output %s",
+                   rollback_index, signing_options.c_str(), include_descriptor_options.c_str(),
+                   chain_partition_options.c_str(), additional_options.c_str(),
+                   vbmeta_image.path.value().c_str());
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+    vbmeta_image.content.resize(file_size);
+    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+    // Stores the generated vbmeta image into vbmeta_images_ member object.
+    vbmeta_images_.emplace(file_name, std::move(vbmeta_image));
+}
+
+void BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
+                                       const std::string& output_file_name,
+                                       const size_t padding_size) {
+    VBMetaImage vbmeta_image;
+    vbmeta_image.path = test_dir_.Append(output_file_name);
+    EXPECT_COMMAND(0,
+                   "avbtool extract_vbmeta_image"
+                   " --image %s"
+                   " --output %s"
+                   " --padding_size %zu",
+                   image_path.value().c_str(), vbmeta_image.path.value().c_str(), padding_size);
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+    vbmeta_image.content.resize(file_size);
+    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+    // Stores the extracted vbmeta image into vbmeta_images_ member object.
+    vbmeta_images_.emplace(output_file_name, std::move(vbmeta_image));
+}
+
+// Generates a file with name |file_name| of size |image_size| with
+// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+base::FilePath BaseFsAvbTest::GenerateImage(const std::string file_name, size_t image_size,
+                                            uint8_t start_byte) {
+    std::vector<uint8_t> image;
+    image.resize(image_size);
+    for (size_t n = 0; n < image_size; n++) {
+        image[n] = uint8_t(n + start_byte);
+    }
+    base::FilePath image_path = test_dir_.Append(file_name);
+    EXPECT_EQ(image_size,
+              static_cast<const size_t>(base::WriteFile(
+                      image_path, reinterpret_cast<const char*>(image.data()), image.size())));
+    return image_path;
+}
+
+void BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
+                                 const std::string& partition_name, const uint64_t partition_size,
+                                 const std::string& avb_algorithm, uint64_t rollback_index,
+                                 const base::FilePath& key_path, const std::string& salt,
+                                 const std::string& additional_options) {
+    // 'add_hash_footer' or 'add_hashtree_footer'.
+    EXPECT_TRUE(footer_type == "hash" or footer_type == "hashtree");
+    std::string add_footer_option = "add_" + footer_type + "_footer";
+
+    std::string signing_options;
+    if (avb_algorithm == "") {
+        signing_options = " --algorithm NONE ";
+    } else {
+        signing_options =
+                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+    }
+    EXPECT_COMMAND(0,
+                   "avbtool %s"
+                   " --image %s"
+                   " --partition_name %s "
+                   " --partition_size %" PRIu64 " --rollback_index %" PRIu64
+                   " --salt %s"
+                   " %s %s",
+                   add_footer_option.c_str(), image_path.value().c_str(), partition_name.c_str(),
+                   partition_size, rollback_index, salt.c_str(), signing_options.c_str(),
+                   additional_options.c_str());
+}
+
+std::string BaseFsAvbTest::InfoImage(const base::FilePath& image_path) {
+    base::FilePath tmp_path = test_dir_.Append("info_output.txt");
+    EXPECT_COMMAND(0, "avbtool info_image --image %s --output %s", image_path.value().c_str(),
+                   tmp_path.value().c_str());
+    std::string info_data;
+    EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
+    return info_data;
+}
+
+std::string BaseFsAvbTest::InfoImage(const std::string& file_name) {
+    auto iter = vbmeta_images_.find(file_name);
+    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
+    // Gets the image path from iterator->second.path: VBMetaImage.path.
+    base::FilePath image_path = iter->second.path;
+    return InfoImage(image_path);
+}
+
+base::FilePath BaseFsAvbTest::ExtractPublicKeyAvb(const base::FilePath& key_path) {
+    std::string file_name = key_path.RemoveExtension().BaseName().value();
+    base::FilePath tmp_path = test_dir_.Append(file_name + "public_key.bin");
+    EXPECT_COMMAND(0,
+                   "avbtool extract_public_key --key %s"
+                   " --output %s",
+                   key_path.value().c_str(), tmp_path.value().c_str());
+    return tmp_path;
+}
+
+std::string BaseFsAvbTest::ExtractPublicKeyAvbBlob(const base::FilePath& key_path) {
+    base::FilePath tmp_path = test_dir_.Append("public_key.bin");
+    EXPECT_COMMAND(0,
+                   "avbtool extract_public_key --key %s"
+                   " --output %s",
+                   key_path.value().c_str(), tmp_path.value().c_str());
+    std::string key_data;
+    EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
+    return key_data;
+}
+
+TEST_F(BaseFsAvbTest, GenerateImage) {
+    const size_t image_size = 5 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    EXPECT_NE(0U, boot_path.value().size());
+
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+
+    // Checks file content is as expected.
+    std::vector<uint8_t> expected_content;
+    expected_content.resize(image_size);
+    for (size_t n = 0; n < image_size; n++) {
+        expected_content[n] = uint8_t(n);
+    }
+    std::vector<uint8_t> actual_content;
+    actual_content.resize(image_size);
+    EXPECT_TRUE(
+            base::ReadFile(boot_path, reinterpret_cast<char*>(actual_content.data()), image_size));
+    EXPECT_EQ(expected_content, actual_content);
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImage) {
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0,
+                        base::FilePath("data/testkey_rsa2048.pem"),
+                        {}, /* include_descriptor_image_paths */
+                        {}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+    EXPECT_EQ("5eba9ad4e775645e7eac441a563c200681ae868158d06f6a6cd36d06c07bd781",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     320 bytes\n"
+            "Auxiliary Block:          576 bytes\n"
+            "Algorithm:                SHA256_RSA2048\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    (none)\n",
+            InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashFooter) {
+    // Generates a raw boot.img
+    const size_t image_size = 5 * 1024 * 1024;
+    const size_t partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    EXPECT_NE(0U, boot_path.value().size());
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+    // Appends AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+                 base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+    ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          1216 bytes\n"
+            "Algorithm:                SHA256_RSA4096\n"
+            "Rollback Index:           10\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n",
+            InfoImage("boot-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashtreeFooter) {
+    // Generates a raw system.img
+    const size_t image_size = 50 * 1024 * 1024;
+    const size_t partition_size = 60 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+    EXPECT_NE(0U, system_path.value().size());
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+                 base::FilePath("data/testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts system vbmeta from system.img into system-vbmeta.img.
+    ExtractVBMetaImage(system_path, "system-vbmeta.img");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          2304 bytes\n"
+            "Algorithm:                SHA512_RSA8192\n"
+            "Rollback Index:           20\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            52428800 bytes\n"
+            "      Tree Offset:           52428800\n"
+            "      Tree Size:             413696 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            52842496\n"
+            "      FEC size:              417792 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           d20d40c02298e385ab6d398a61a3b91dc9947d99\n"
+            "      Flags:                 0\n",
+            InfoImage("system-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA4096", 10,
+                 base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA8192", 20,
+                 base::FilePath("data/testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta.img including both 'boot' and 'system' descriptors.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0,
+                        base::FilePath("data/testkey_rsa2048.pem"),
+                        {boot_path, system_path}, /* include_descriptor_image_paths */
+                        {},                       /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+    EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     320 bytes\n"
+            "Auxiliary Block:          960 bytes\n"
+            "Algorithm:                SHA256_RSA2048\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            10485760 bytes\n"
+            "      Tree Offset:           10485760\n"
+            "      Tree Size:             86016 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            10571776\n"
+            "      FEC size:              90112 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+            "      Flags:                 0\n",
+            InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 base::FilePath("data/testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Make a vbmeta image with chain partitions.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(base::FilePath("data/testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(base::FilePath("data/testkey_rsa4096.pem"));
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                        base::FilePath("data/testkey_rsa8192.pem"),
+                        {},                               /* include_descriptor_image_paths */
+                        {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                         {"system", 2, rsa4096_public_key}},
+                        "--internal_release_string \"unit test\"");
+
+    // vbmeta digest calculation includes the chained vbmeta from boot.img and system.img.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          3840 bytes\n"
+            "Algorithm:                SHA256_RSA8192\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          boot\n"
+            "      Rollback Index Location: 1\n"
+            "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          system\n"
+            "      Rollback Index Location: 2\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta.img"));
+}
+
+}  // namespace fs_avb_host_test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.h b/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.h
new file mode 100644
index 0000000..f329466
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+// Utility macro to run the command expressed by the printf()-style string
+// |command_format| using the system(3) utility function. Will assert unless
+// the command exits normally with exit status |expected_exit_status|.
+#define EXPECT_COMMAND(expected_exit_status, command_format, ...)                   \
+    do {                                                                            \
+        int rc = system(base::StringPrintf(command_format, ##__VA_ARGS__).c_str()); \
+        EXPECT_TRUE(WIFEXITED(rc));                                                 \
+        EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status);                           \
+    } while (0);
+
+namespace fs_avb_host_test {
+
+struct VBMetaImage {
+    // Path to vbmeta image generated with GenerateVBMetaImage().
+    base::FilePath path;
+    // Contents of the image generated with GenerateVBMetaImage().
+    std::vector<uint8_t> content;
+};
+
+struct ChainPartitionConfig {
+    std::string partition_name;
+    uint32_t rollback_index_location;
+    base::FilePath key_blob_path;
+};
+
+/* Base-class used for unit test. */
+class BaseFsAvbTest : public ::testing::Test {
+  public:
+    BaseFsAvbTest() {}
+
+  protected:
+    virtual ~BaseFsAvbTest() {}
+
+    // Calculates the vbmeta digest using 'avbtool calc_vbmeta_digest' command.
+    // Note that the calculation includes chained vbmeta images.
+    std::string CalcVBMetaDigest(const std::string& file_name, const std::string& hash_algorithm);
+
+    // Generates a vbmeta image with |file_name| by avbtool.
+    // The generated vbmeta image will be written to disk, see the
+    // |vbmeta_images_| variable for its path and the content.
+    void GenerateVBMetaImage(const std::string& file_name, const std::string& avb_algorithm,
+                             uint64_t rollback_index, const base::FilePath& key_path,
+                             const std::vector<base::FilePath>& include_descriptor_image_paths,
+                             const std::vector<ChainPartitionConfig>& chain_partitions,
+                             const std::string& additional_options = "");
+    // Similar to above, but extracts a vbmeta image from the given image_path.
+    // The extracted vbmeta image will be written to disk, with |output_file_name|.
+    // See the |vbmeta_images_| variable for its path and the content.
+    void ExtractVBMetaImage(const base::FilePath& image_path, const std::string& output_file_name,
+                            const size_t padding_size = 0);
+
+    // Generate a file with name |file_name| of size |image_size| with
+    // known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+    base::FilePath GenerateImage(const std::string file_name, size_t image_size,
+                                 uint8_t start_byte = 0);
+    // Invokes 'avbtool add_hash_footer' or 'avbtool add_hashtree_footer' to sign
+    // the |image_path|. The |footer_type| can be either "hash" or "hashtree".
+    void AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
+                      const std::string& partition_name, const uint64_t partition_size,
+                      const std::string& avb_algorithm, uint64_t rollback_index,
+                      const base::FilePath& key_path, const std::string& salt = "d00df00d",
+                      const std::string& additional_options = "");
+
+    // Returns the output of 'avbtool info_image' for the |image_path|.
+    std::string InfoImage(const base::FilePath& image_path);
+    // Same as above, but for an internal vbmeta image with |file_name| in |vbmeta_images_|.
+    std::string InfoImage(const std::string& file_name);
+
+    // Extracts public key blob in AVB format for a .pem key, then returns the
+    // file path: a .bin file.
+    base::FilePath ExtractPublicKeyAvb(const base::FilePath& key_path);
+    // Same as above, but returns the key blob binary instead.
+    std::string ExtractPublicKeyAvbBlob(const base::FilePath& key_path);
+
+    void SetUp() override;
+    void TearDown() override;
+
+    // Temporary directory created in SetUp().
+    base::FilePath test_dir_;
+    // Maps vbmeta image name (e.g., vbmeta_a.img, system_a.img) to VBMetaImage.
+    std::map<std::string, VBMetaImage> vbmeta_images_;
+};
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 07f9d66..07e3c8a 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -212,7 +212,7 @@
     sABOverrideValue = ab_device;
 }
 
-MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false), ignore_slot_suffixing_(false) {
     memset(&geometry_, 0, sizeof(geometry_));
     geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
     geometry_.struct_size = sizeof(geometry_);
@@ -436,7 +436,7 @@
         LERROR << "Could not find partition group: " << group_name;
         return nullptr;
     }
-    if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" &&
+    if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" && !ignore_slot_suffixing_ &&
         GetPartitionSlotSuffix(name).empty()) {
         LERROR << "Unsuffixed partition not allowed on A/B device: " << name;
         return nullptr;
@@ -972,6 +972,10 @@
     auto_slot_suffixing_ = true;
 }
 
+void MetadataBuilder::IgnoreSlotSuffixing() {
+    ignore_slot_suffixing_ = true;
+}
+
 bool MetadataBuilder::IsABDevice() const {
     if (sABOverrideSet) {
         return sABOverrideValue;
@@ -983,5 +987,18 @@
     return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
 }
 
+bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
+                                      uint64_t num_sectors, uint64_t physical_sector) {
+    uint32_t device_index;
+    if (!FindBlockDeviceByName(block_device, &device_index)) {
+        LERROR << "Could not find backing block device for extent: " << block_device;
+        return false;
+    }
+
+    auto extent = std::make_unique<LinearExtent>(num_sectors, device_index, physical_sector);
+    partition->AddExtent(std::move(extent));
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f477b4b..57cce21 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -219,6 +219,10 @@
     // Find a group by name. If no group is found, nullptr is returned.
     PartitionGroup* FindGroup(const std::string& name);
 
+    // Add a predetermined extent to a partition.
+    bool AddLinearExtent(Partition* partition, const std::string& block_device,
+                         uint64_t num_sectors, uint64_t physical_sector);
+
     // Grow or shrink a partition to the requested size. This size will be
     // rounded UP to the nearest block (512 bytes).
     //
@@ -244,6 +248,9 @@
     // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
     void SetAutoSlotSuffixing();
 
+    // If set, checks for slot suffixes will be ignored internally.
+    void IgnoreSlotSuffixing();
+
     bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
     bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
 
@@ -306,6 +313,7 @@
     std::vector<std::unique_ptr<PartitionGroup>> groups_;
     std::vector<LpMetadataBlockDevice> block_devices_;
     bool auto_slot_suffixing_;
+    bool ignore_slot_suffixing_;
 };
 
 // Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 9e211e3..160e076 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -49,7 +49,7 @@
 Returns: true if device is in adb mode" ]
 inAdb() {
   adb devices |
-    grep -v 'List of devices attached' |
+    grep -v -e 'List of devices attached' -e '^$' |
     if [ -n "${ANDROID_SERIAL}" ]; then
       grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
     else
@@ -61,7 +61,17 @@
 
 Returns: true if the command succeeded" ]
 adb_sh() {
-  adb shell "${@}"
+  args=
+  for i in ${@}; do
+    if [ X"${i}" != X"${i#\'}" ]; then
+      args="${args} ${i}"
+    elif [ X"${i}" != X"${i#* }" ]; then
+      args="${args} '${i}'"
+    else
+      args="${args} ${i}"
+    fi
+  done
+  adb shell ${args}
 }
 
 [ "USAGE: adb_date >/dev/stdout
@@ -92,7 +102,7 @@
 
 Returns: true if device is (likely) a debug build" ]
 isDebuggable() {
-  if inAdb && [ 1 -ne "`get_property ro.debuggable`" ]; then
+  if inAdb && [ 1 != "`get_property ro.debuggable`" ]; then
     false
   fi
 }
@@ -363,8 +373,12 @@
   echo "${GREEN}[       OK ]${NORMAL} no overlay present before setup" >&2
 overlayfs_needed=true
 D=`adb_sh cat /proc/mounts </dev/null |
-   skip_administrative_mounts data |
-   cut -s -d' ' -f1`
+   skip_administrative_mounts data`
+if echo "${D}" | grep /dev/root >/dev/null; then
+  D=`echo / /
+     echo "${D}" | grep -v /dev/root`
+fi
+D=`echo "${D}" | cut -s -d' ' -f1`
 D=`adb_sh df -k ${D} </dev/null`
 echo "${D}"
 if [ X"${D}" = X"${D##* 100[%] }" ]; then
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index f78093b..3b6ff9b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -36,6 +36,8 @@
 #include <string>
 #include <vector>
 
+using namespace std::literals::string_literals;
+
 using DeviceMapper = ::android::dm::DeviceMapper;
 using DmTable = ::android::dm::DmTable;
 using DmTarget = ::android::dm::DmTarget;
@@ -51,7 +53,7 @@
     std::cerr << "commands:" << std::endl;
     std::cerr << "  create <dm-name> [-ro] <targets...>" << std::endl;
     std::cerr << "  delete <dm-name>" << std::endl;
-    std::cerr << "  list <devices | targets>" << std::endl;
+    std::cerr << "  list <devices | targets> [-v]" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
     std::cerr << "  table <dm-name>" << std::endl;
     std::cerr << "  help" << std::endl;
@@ -197,7 +199,8 @@
     return 0;
 }
 
-static int DmListTargets(DeviceMapper& dm) {
+static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
+                         [[maybe_unused]] char** argv) {
     std::vector<DmTargetTypeInfo> targets;
     if (!dm.GetAvailableTargets(&targets)) {
         std::cerr << "Failed to read available device mapper targets" << std::endl;
@@ -218,7 +221,7 @@
     return 0;
 }
 
-static int DmListDevices(DeviceMapper& dm) {
+static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
     std::vector<DmBlockDevice> devices;
     if (!dm.GetAvailableDevices(&devices)) {
         std::cerr << "Failed to read available device mapper devices" << std::endl;
@@ -230,15 +233,37 @@
         return 0;
     }
 
+    bool verbose = (argc && (argv[0] == "-v"s));
     for (const auto& dev : devices) {
         std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
                   << dev.Minor() << std::endl;
+        if (verbose) {
+            std::vector<DeviceMapper::TargetInfo> table;
+            if (!dm.GetTableInfo(dev.name(), &table)) {
+                std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
+                          << std::endl;
+                return -EINVAL;
+            }
+
+            uint32_t target_num = 1;
+            for (const auto& target : table) {
+                std::cout << "  target#" << target_num << ": ";
+                std::cout << target.spec.sector_start << "-"
+                          << (target.spec.sector_start + target.spec.length) << ": "
+                          << target.spec.target_type;
+                if (!target.data.empty()) {
+                    std::cout << ", " << target.data;
+                }
+                std::cout << std::endl;
+                target_num++;
+            }
+        }
     }
 
     return 0;
 }
 
-static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
         {"targets", DmListTargets},
         {"devices", DmListDevices},
 };
@@ -251,7 +276,7 @@
 
     DeviceMapper& dm = DeviceMapper::Instance();
     for (const auto& l : listmap) {
-        if (l.first == argv[0]) return l.second(dm);
+        if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
     }
 
     std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
index dca0ccc..6960c5d 100644
--- a/healthd/android.hardware.health@2.0-service.rc
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -2,4 +2,5 @@
     class hal
     user system
     group system
+    capabilities WAKE_ALARM
     file /dev/kmsg w
diff --git a/init/README.md b/init/README.md
index 5c07ac1..8d47e0a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -813,3 +813,42 @@
 SELinux would permit the operation, or if the UIDs and GIDs resolve.
 2) No checking if a service exists or has a valid SELinux domain defined
 3) No checking if a service has not been previously defined in a different init script.
+
+Early Init Boot Sequence
+------------------------
+The early init boot sequence is broken up into three stages: first stage init, SELinux setup, and
+second stage init.
+
+First stage init is responsible for setting up the bare minimum requirements to load the rest of the
+system. Specifically this includes mounting /dev, /proc, mounting 'early mount' partitions (which
+needs to include all partitions that contain system code, for example system and vendor), and moving
+the system.img mount to / for devices with a ramdisk.
+
+Note that in Android Q, system.img always contains TARGET_ROOT_OUT and always is mounted at / by the
+time first stage init finishes. Android Q will also require dynamic partitions and therefore will
+require using a ramdisk to boot Android. The recovery ramdisk can be used to boot to Android instead
+of a dedicated ramdisk as well.
+
+First stage init has three variations depending on the device configuration:
+1) For system-as-root devices, first stage init is part of /system/bin/init and a symlink at /init
+points to /system/bin/init for backwards compatibility. These devices do not need to do anything to
+mount system.img, since it is by definition already mounted as the rootfs by the kernel.
+
+2) For devices with a ramdisk, first stage init is a static executable located at /init. These
+devices mount system.img as /system then perform a switch root operation to move the mount at
+/system to /. The contents of the ramdisk are freed after mounting has completed.
+
+3) For devices that use recovery as a ramdisk, first stage init it contained within the shared init
+located at /init within the recovery ramdisk. These devices first switch root to
+/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
+as 2). Note that the decision to boot normally into Android instead of booting
+into recovery mode is made if androidboot.force_normal_boot=1 is present in the
+kernel commandline.
+
+Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
+phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
+information on the specifics of this process.
+
+Lastly once that phase finishes, it execs /system/bin/init again with the "second_stage"
+argument. At this point the main phase of init runs and continues the boot process via the init.rc
+scripts.
diff --git a/init/devices.cpp b/init/devices.cpp
index 45b17a2..1a77ba1 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -320,6 +320,7 @@
 
     auto link_path = "/dev/block/" + type + "/" + device;
 
+    bool is_boot_device = boot_devices_.find(device) != boot_devices_.end();
     if (!uevent.partition_name.empty()) {
         std::string partition_name_sanitized(uevent.partition_name);
         SanitizePartitionName(&partition_name_sanitized);
@@ -329,9 +330,13 @@
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
         // Adds symlink: /dev/block/by-name/<partition_name>.
-        if (boot_devices_.find(device) != boot_devices_.end()) {
+        if (is_boot_device) {
             links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
         }
+    } else if (is_boot_device) {
+        // If we don't have a partition name but we are a partition on a boot device, create a
+        // symlink of /dev/block/by-name/<device_name> for symmetry.
+        links.emplace_back("/dev/block/by-name/" + uevent.device_name);
     }
 
     auto last_slash = uevent.path.rfind('/');
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index acefdf0..b5ff658 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -395,6 +395,11 @@
         return false;
     }
     if (fs_mgr_do_mount_one(*fstab_entry)) {
+        if (fstab_entry->fs_mgr_flags.formattable) {
+            PLOG(INFO) << "Failed to mount '" << fstab_entry->mount_point << "', "
+                       << "ignoring mount for formattable partition";
+            return true;
+        }
         PLOG(ERROR) << "Failed to mount '" << fstab_entry->mount_point << "'";
         return false;
     }
diff --git a/init/service.cpp b/init/service.cpp
index 5aa3764..272809f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -367,12 +367,19 @@
         return;
     }
 
-    // If we crash > 4 times in 4 minutes, reboot into bootloader.
+    // If we crash > 4 times in 4 minutes, reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
-    if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
+    if (((flags_ & SVC_CRITICAL) || classnames_.count("updatable")) && !(flags_ & SVC_RESTART)) {
         if (now < time_crashed_ + 4min) {
             if (++crash_count_ > 4) {
-                LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+                if (flags_ & SVC_CRITICAL) {
+                    // Aborts into bootloader
+                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+                } else {
+                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times in 4 minutes";
+                    // Notifies update_verifier and apexd
+                    property_set("ro.init.updatable_crashing", "1");
+                }
             }
         } else {
             time_crashed_ = now;
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 37afb98..4291212 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -89,6 +89,7 @@
                 "socket_inaddr_any_server_windows.cpp",
                 "socket_network_client_windows.cpp",
                 "sockets_windows.cpp",
+                "trace-host.cpp",
             ],
 
             enabled: true,
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index 715d63d..1f93494 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -13,11 +13,24 @@
 // limitations under the License.
 
 cc_binary {
+    name: "librank2",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: ["librank.cpp"],
+    shared_libs: [
+        "libbase",
+        "libmeminfo",
+    ],
+}
+
+cc_binary {
     name: "procmem2",
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wno-unused-parameter",
     ],
 
     srcs: ["procmem.cpp"],
@@ -32,7 +45,6 @@
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wno-unused-parameter",
     ],
 
     srcs: ["procrank.cpp"],
diff --git a/libmeminfo/tools/librank.cpp b/libmeminfo/tools/librank.cpp
new file mode 100644
index 0000000..2c2583d
--- /dev/null
+++ b/libmeminfo/tools/librank.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2018 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 <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
+            "\n"
+            "Sort options:\n"
+            "    -v  Sort processes by VSS.\n"
+            "    -r  Sort processes by RSS.\n"
+            "    -p  Sort processes by PSS.\n"
+            "    -u  Sort processes by USS.\n"
+            "    -s  Sort processes by swap.\n"
+            "        (Default sort order is PSS.)\n"
+            "    -a  Show all mappings, including stack, heap and anon.\n"
+            "    -P /path  Limit libraries displayed to those in path.\n"
+            "    -R  Reverse sort order (default is descending).\n"
+            "    -m [r][w][x] Only list pages that exactly match permissions\n"
+            "    -c  Only show cached (storage backed) pages\n"
+            "    -C  Only show non-cached (ram/swap backed) pages\n"
+            "    -k  Only show pages collapsed by KSM\n"
+            "    -h  Display this help screen.\n",
+            getprogname());
+    exit(exit_status);
+}
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+    to->vss += from.vss;
+    to->rss += from.rss;
+    to->pss += from.pss;
+    to->uss += from.uss;
+
+    to->swap += from.swap;
+
+    to->private_clean += from.private_clean;
+    to->private_dirty += from.private_dirty;
+
+    to->shared_clean += from.shared_clean;
+    to->shared_dirty += from.shared_dirty;
+}
+
+struct ProcessRecord {
+  public:
+    ProcessRecord(pid_t pid) : pid_(-1), cmdline_("") {
+        std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+        std::string cmdline;
+        if (!::android::base::ReadFileToString(fname, &cmdline)) {
+            fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+            return;
+        }
+        // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+        // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+        // e.g. xtra-daemon, lowi-server
+        // The .c_str() assignment below then takes care of trimming the cmdline at the first
+        // 0x00. This is how original procrank worked (luckily)
+        cmdline_ = cmdline.c_str();
+        pid_ = pid;
+        usage_.clear();
+    }
+
+    ~ProcessRecord() = default;
+
+    bool valid() const { return pid_ != -1; }
+
+    // Getters
+    pid_t pid() const { return pid_; }
+    const std::string& cmdline() const { return cmdline_; }
+    const MemUsage& usage() const { return usage_; }
+
+    // Add to the usage
+    void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
+
+  private:
+    pid_t pid_;
+    std::string cmdline_;
+    MemUsage usage_;
+};
+
+struct LibRecord {
+  public:
+    LibRecord(const std::string& name) : name_(name) {}
+    ~LibRecord() = default;
+
+    const std::string& name() const { return name_; }
+    const MemUsage& usage() const { return usage_; }
+    const std::vector<ProcessRecord>& processes() const { return procs_; }
+    uint64_t pss() const { return usage_.pss; }
+    void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
+        auto process = std::find_if(procs_.begin(), procs_.end(),
+                                    [&](auto p) -> bool { return p.pid() == proc.pid(); });
+        if (process == procs_.end()) {
+            process = procs_.emplace(procs_.end(), proc.pid());
+        }
+        process->AddUsage(mem_usage);
+        add_mem_usage(&usage_, mem_usage);
+    }
+
+    void Sort(std::function<bool(const ProcessRecord&, const ProcessRecord&)>& sorter) {
+        std::sort(procs_.begin(), procs_.end(), sorter);
+    }
+
+  private:
+    std::string name_;
+    MemUsage usage_;
+    std::vector<ProcessRecord> procs_;
+};
+
+// List of every library / map
+static std::vector<LibRecord> g_libs;
+
+// List of library/map names that we don't want to show by default
+static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
+
+// Global flags affected by command line
+static uint64_t g_pgflags = 0;
+static uint64_t g_pgflags_mask = 0;
+static uint16_t g_mapflags_mask = 0;
+static bool g_all_libs = false;
+static bool g_has_swap = false;
+static bool g_reverse_sort = false;
+static std::string g_prefix_filter = "";
+
+static bool read_all_pids(std::function<bool(pid_t pid)> for_each_pid) {
+    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+    if (!procdir) return false;
+
+    struct dirent* dir;
+    pid_t pid;
+    while ((dir = readdir(procdir.get()))) {
+        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+        if (!for_each_pid(pid)) return false;
+    }
+
+    return true;
+}
+
+static bool scan_libs_per_process(pid_t pid) {
+    ProcMemInfo pmem(pid, false, g_pgflags, g_pgflags_mask);
+    const std::vector<Vma> maps = pmem.Maps();
+    if (maps.size() == 0) {
+        // nothing to do here, continue
+        return true;
+    }
+
+    ProcessRecord proc(pid);
+    if (!proc.valid()) {
+        fprintf(stderr, "Failed to create process record for process: %d\n", pid);
+        return false;
+    }
+
+    for (auto& map : maps) {
+        // skip library / map if prefix for the path doesn't match
+        if (!g_prefix_filter.empty() && !::android::base::StartsWith(map.name, g_prefix_filter)) {
+            continue;
+        }
+        // Skip maps based on map permissions
+        if (g_mapflags_mask &&
+            ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != g_mapflags_mask)) {
+            continue;
+        }
+
+        // skip blacklisted library / map names
+        if (!g_all_libs && (std::find(g_blacklisted_libs.begin(), g_blacklisted_libs.end(),
+                                      map.name) != g_blacklisted_libs.end())) {
+            continue;
+        }
+
+        auto lib = std::find_if(g_libs.begin(), g_libs.end(),
+                                [&](auto l) -> bool { return map.name == l.name(); });
+        if (lib == g_libs.end()) {
+            lib = g_libs.emplace(g_libs.end(), map.name);
+        }
+
+        lib->AddUsage(proc, map.usage);
+        if (!g_has_swap && map.usage.swap) {
+            g_has_swap = true;
+        }
+    }
+
+    return true;
+}
+
+static uint16_t parse_mapflags(const char* mapflags) {
+    uint16_t ret = 0;
+    for (const char* p = mapflags; *p; p++) {
+        switch (*p) {
+            case 'r':
+                ret |= PROT_READ;
+                break;
+            case 'w':
+                ret |= PROT_WRITE;
+                break;
+            case 'x':
+                ret |= PROT_EXEC;
+                break;
+            default:
+                error(EXIT_FAILURE, 0, "Invalid permissions string: %s, %s", mapflags, p);
+        }
+    }
+
+    return ret;
+}
+
+int main(int argc, char* argv[]) {
+    int opt;
+
+    auto pss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().pss < b.usage().pss : a.usage().pss > b.usage().pss;
+    };
+
+    auto uss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().uss < b.usage().uss : a.usage().uss > b.usage().uss;
+    };
+
+    auto vss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().vss < b.usage().vss : a.usage().vss > b.usage().vss;
+    };
+
+    auto rss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().rss < b.usage().rss : a.usage().rss > b.usage().rss;
+    };
+
+    auto swap_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+        return g_reverse_sort ? a.usage().swap < b.usage().swap : a.usage().swap > b.usage().swap;
+    };
+
+    std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
+
+    while ((opt = getopt(argc, argv, "acChkm:pP:uvrsR")) != -1) {
+        switch (opt) {
+            case 'a':
+                g_all_libs = true;
+                break;
+            case 'c':
+                g_pgflags = 0;
+                g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'C':
+                g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            case 'k':
+                g_pgflags = g_pgflags_mask = (1 << KPF_KSM);
+                break;
+            case 'm':
+                g_mapflags_mask = parse_mapflags(optarg);
+                break;
+            case 'p':
+                sort_func = pss_sort;
+                break;
+            case 'P':
+                g_prefix_filter = optarg;
+                break;
+            case 'u':
+                sort_func = uss_sort;
+                break;
+            case 'v':
+                sort_func = vss_sort;
+                break;
+            case 'r':
+                sort_func = rss_sort;
+                break;
+            case 's':
+                sort_func = swap_sort;
+                break;
+            case 'R':
+                g_reverse_sort = true;
+                break;
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    if (!read_all_pids(scan_libs_per_process)) {
+        error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
+    }
+
+    printf(" %6s   %7s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
+    if (g_has_swap) {
+        printf(" %6s  ", "Swap");
+    }
+    printf("Name/PID\n");
+
+    // sort the libraries by their pss
+    std::sort(g_libs.begin(), g_libs.end(),
+              [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
+
+    for (auto& lib : g_libs) {
+        printf("%6" PRIu64 "K   %7s   %6s   %6s   %6s  ", lib.pss() / 1024, "", "", "", "");
+        if (g_has_swap) {
+            printf(" %6s  ", "");
+        }
+        printf("%s\n", lib.name().c_str());
+
+        // sort all mappings first
+        lib.Sort(sort_func);
+
+        for (auto& p : lib.processes()) {
+            const MemUsage& usage = p.usage();
+            printf(" %6s  %7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
+                   usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
+            if (g_has_swap) {
+                printf("%6" PRIu64 "K  ", usage.swap / 1024);
+            }
+            printf("  %s [%d]\n", p.cmdline().c_str(), p.pid());
+        }
+    }
+
+    return 0;
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
index 56de12d..b9b174d 100644
--- a/libmeminfo/tools/procmem.cpp
+++ b/libmeminfo/tools/procmem.cpp
@@ -42,7 +42,7 @@
 // Show working set, mutually exclusive with reset_wss;
 bool show_wss = false;
 
-static void usage(const char* cmd) {
+[[noreturn]] static void usage(int exit_status) {
     fprintf(stderr,
             "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
             "    -i  Uses idle page tracking for working set statistics.\n"
@@ -52,7 +52,9 @@
             "    -u  Sort by USS.\n"
             "    -m  Sort by mapping order (as read from /proc).\n"
             "    -h  Hide maps with no RSS.\n",
-            cmd);
+            getprogname());
+
+    exit(exit_status);
 }
 
 static void print_separator(std::stringstream& ss) {
@@ -154,17 +156,15 @@
                 show_wss = true;
                 break;
             case '?':
-                usage(argv[0]);
-                return 0;
+                usage(EXIT_SUCCESS);
             default:
-                abort();
+                usage(EXIT_FAILURE);
         }
     }
 
     if (optind != (argc - 1)) {
         fprintf(stderr, "Need exactly one pid at the end\n");
-        usage(argv[0]);
-        exit(EXIT_FAILURE);
+        usage(EXIT_FAILURE);
     }
 
     pid_t pid = atoi(argv[optind]);
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
index e39e7fa..a751722 100644
--- a/libmeminfo/tools/procrank.cpp
+++ b/libmeminfo/tools/procrank.cpp
@@ -142,8 +142,9 @@
 uint64_t total_uswap = 0;
 uint64_t total_zswap = 0;
 
-static void usage(const char* myname) {
-    std::cerr << "Usage: " << myname << " [ -W ] [ -v | -r | -p | -u | -s | -h ]" << std::endl
+[[noreturn]] static void usage(int exit_status) {
+    std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ]"
+              << std::endl
               << "    -v  Sort by VSS." << std::endl
               << "    -r  Sort by RSS." << std::endl
               << "    -p  Sort by PSS." << std::endl
@@ -159,6 +160,7 @@
               << "    -o  Show and sort by oom score against lowmemorykiller thresholds."
               << std::endl
               << "    -h  Display this help screen." << std::endl;
+    exit(exit_status);
 }
 
 static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) {
@@ -386,9 +388,7 @@
                 pgflags_mask = (1 << KPF_SWAPBACKED);
                 break;
             case 'h':
-                usage(argv[0]);
-                return 0;
-                break;
+                usage(EXIT_SUCCESS);
             case 'k':
                 pgflags = (1 << KPF_KSM);
                 pgflags_mask = (1 << KPF_KSM);
@@ -422,7 +422,7 @@
                 reset_wss = true;
                 break;
             default:
-                abort();
+                usage(EXIT_FAILURE);
         }
     }
 
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 4e0470e..89d4fc0 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -94,7 +94,10 @@
                 "DexFile.cpp",
                 "DexFiles.cpp",
             ],
-            exclude_shared_libs: ["libdexfile"],
+            exclude_shared_libs: [
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
         },
         recovery: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
@@ -102,7 +105,10 @@
                 "DexFile.cpp",
                 "DexFiles.cpp",
             ],
-            exclude_shared_libs: ["libdexfile"],
+            exclude_shared_libs: [
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
         },
     },
 
@@ -127,7 +133,8 @@
 
     shared_libs: [
         "libbase",
-        "libdexfile",
+        "libdexfile_external",
+        "libdexfile_support",
         "liblog",
         "liblzma",
     ],
@@ -215,6 +222,7 @@
         "liblzma",
         "libunwindstack",
         "libdexfile",
+        "libdexfile_support",
     ],
 
     static_libs: [
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index 8ec560c..9b0b232 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -23,13 +23,7 @@
 #include <memory>
 
 #include <android-base/unique_fd.h>
-
-#include <dex/class_accessor-inl.h>
-#include <dex/code_item_accessors-inl.h>
-#include <dex/compact_dex_file.h>
-#include <dex/dex_file-inl.h>
-#include <dex/dex_file_loader.h>
-#include <dex/standard_dex_file.h>
+#include <art_api/ext_dex_file.h>
 
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
@@ -38,169 +32,71 @@
 
 namespace unwindstack {
 
-DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) {
+std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+                                         MapInfo* info) {
   if (!info->name.empty()) {
-    std::unique_ptr<DexFileFromFile> dex_file(new DexFileFromFile);
-    if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
-      return dex_file.release();
+    std::unique_ptr<DexFile> dex_file =
+        DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
+    if (dex_file) {
+      return dex_file;
     }
   }
-
-  std::unique_ptr<DexFileFromMemory> dex_file(new DexFileFromMemory);
-  if (dex_file->Open(dex_file_offset_in_memory, memory)) {
-    return dex_file.release();
-  }
-  return nullptr;
-}
-
-DexFileFromFile::~DexFileFromFile() {
-  if (size_ != 0) {
-    munmap(mapped_memory_, size_);
-  }
+  return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
 }
 
 bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
                                    uint64_t* method_offset) {
-  if (dex_file_ == nullptr) {
+  art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset);
+  if (method_info.offset == 0) {
     return false;
   }
-
-  if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
-    return false;  // The DEX offset is not within the bytecode of this dex file.
-  }
-
-  if (dex_file_->IsCompactDexFile()) {
-    // The data section of compact dex files might be shared.
-    // Check the subrange unique to this compact dex.
-    const auto& cdex_header = dex_file_->AsCompactDexFile()->GetHeader();
-    uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
-    uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
-    if (dex_offset < begin || dex_offset >= end) {
-      return false;  // The DEX offset is not within the bytecode of this dex file.
-    }
-  }
-
-  // The method data is cached in a std::map indexed by method end offset and
-  // contains the start offset and the method member index.
-  // Only cache the method data as it is searched. Do not read the entire
-  // set of method data into the cache at once.
-  // This is done because many unwinds only find a single frame with dex file
-  // info, so reading the entire method data is wasteful. However, still cache
-  // the data so that anything doing multiple unwinds will have this data
-  // cached for future use.
-
-  // First look in the method cache.
-  auto entry = method_cache_.upper_bound(dex_offset);
-  if (entry != method_cache_.end() && dex_offset >= entry->second.first) {
-    *method_name = dex_file_->PrettyMethod(entry->second.second, false);
-    *method_offset = dex_offset - entry->second.first;
-    return true;
-  }
-
-  // Check the methods we haven't cached.
-  for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) {
-    art::ClassAccessor accessor(*dex_file_, dex_file_->GetClassDef(class_def_index_));
-
-    for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
-      art::CodeItemInstructionAccessor code = method.GetInstructions();
-      if (!code.HasCodeItem()) {
-        continue;
-      }
-      uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
-      uint32_t offset_end = offset + code.InsnsSizeInBytes();
-      uint32_t member_index = method.GetIndex();
-      method_cache_[offset_end] = std::make_pair(offset, member_index);
-      if (offset <= dex_offset && dex_offset < offset_end) {
-        *method_name = dex_file_->PrettyMethod(member_index, false);
-        *method_offset = dex_offset - offset;
-        return true;
-      }
-    }
-  }
-  return false;
+  *method_name = method_info.name;
+  *method_offset = dex_offset - method_info.offset;
+  return true;
 }
 
-bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
+std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(uint64_t dex_file_offset_in_file,
+                                                         const std::string& file) {
   android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
   if (fd == -1) {
-    return false;
-  }
-  struct stat buf;
-  if (fstat(fd, &buf) == -1) {
-    return false;
-  }
-  uint64_t length;
-  if (buf.st_size < 0 ||
-      __builtin_add_overflow(dex_file_offset_in_file, sizeof(art::DexFile::Header), &length) ||
-      static_cast<uint64_t>(buf.st_size) < length) {
-    return false;
+    return nullptr;
   }
 
-  mapped_memory_ = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-  if (mapped_memory_ == MAP_FAILED) {
-    return false;
-  }
-  size_ = buf.st_size;
-
-  uint8_t* memory = reinterpret_cast<uint8_t*>(mapped_memory_);
-
-  art::DexFile::Header* header =
-      reinterpret_cast<art::DexFile::Header*>(&memory[dex_file_offset_in_file]);
-  if (!art::StandardDexFile::IsMagicValid(header->magic_) &&
-      !art::CompactDexFile::IsMagicValid(header->magic_)) {
-    return false;
-  }
-
-  if (__builtin_add_overflow(dex_file_offset_in_file, header->file_size_, &length) ||
-      static_cast<uint64_t>(buf.st_size) < length) {
-    return false;
-  }
-
-  art::DexFileLoader loader;
   std::string error_msg;
-  auto dex = loader.Open(&memory[dex_file_offset_in_file], header->file_size_, "", 0, nullptr,
-                         false, false, &error_msg);
-  dex_file_.reset(dex.release());
-  return dex_file_ != nullptr;
+  std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+      OpenFromFd(fd, dex_file_offset_in_file, file, &error_msg);
+  if (art_dex_file == nullptr) {
+    return nullptr;
+  }
+
+  return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(std::move(*art_dex_file.release())));
 }
 
-bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
-  memory_.resize(sizeof(art::DexFile::Header));
-  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
-    return false;
-  }
+std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
+                                                             Memory* memory,
+                                                             const std::string& name) {
+  std::vector<uint8_t> backing_memory;
 
-  art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
-  uint32_t file_size = header->file_size_;
-  if (art::CompactDexFile::IsMagicValid(header->magic_)) {
-    // Compact dex file store data section separately so that it can be shared.
-    // Therefore we need to extend the read memory range to include it.
-    // TODO: This might be wasteful as we might read data in between as well.
-    //       In practice, this should be fine, as such sharing only happens on disk.
-    uint32_t computed_file_size;
-    if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
-      return false;
+  for (size_t size = 0;;) {
+    std::string error_msg;
+    std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+        OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
+
+    if (art_dex_file != nullptr) {
+      return std::unique_ptr<DexFileFromMemory>(
+          new DexFileFromMemory(std::move(*art_dex_file.release()), std::move(backing_memory)));
     }
-    if (computed_file_size > file_size) {
-      file_size = computed_file_size;
+
+    if (!error_msg.empty()) {
+      return nullptr;
     }
-  } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
-    return false;
+
+    backing_memory.resize(size);
+    if (!memory->ReadFully(dex_file_offset_in_memory, backing_memory.data(),
+                           backing_memory.size())) {
+      return nullptr;
+    }
   }
-
-  memory_.resize(file_size);
-  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
-    return false;
-  }
-
-  header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
-
-  art::DexFileLoader loader;
-  std::string error_msg;
-  auto dex =
-      loader.Open(memory_.data(), header->file_size_, "", 0, nullptr, false, false, &error_msg);
-  dex_file_.reset(dex.release());
-  return dex_file_ != nullptr;
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index c123158..5797dee 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -25,48 +25,41 @@
 #include <utility>
 #include <vector>
 
-#include <dex/dex_file-inl.h>
+#include <art_api/ext_dex_file.h>
 
 namespace unwindstack {
 
-class DexFile {
+class DexFile : protected art_api::dex::DexFile {
  public:
-  DexFile() = default;
   virtual ~DexFile() = default;
 
   bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
 
-  static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
+  static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+                                         MapInfo* info);
 
  protected:
-  void Init();
-
-  std::unique_ptr<const art::DexFile> dex_file_;
-  std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_;  // dex offset to method index.
-
-  uint32_t class_def_index_ = 0;
+  DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {}
 };
 
 class DexFileFromFile : public DexFile {
  public:
-  DexFileFromFile() = default;
-  virtual ~DexFileFromFile();
-
-  bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
+  static std::unique_ptr<DexFileFromFile> Create(uint64_t dex_file_offset_in_file,
+                                                 const std::string& file);
 
  private:
-  void* mapped_memory_ = nullptr;
-  size_t size_ = 0;
+  DexFileFromFile(art_api::dex::DexFile&& art_dex_file) : DexFile(std::move(art_dex_file)) {}
 };
 
 class DexFileFromMemory : public DexFile {
  public:
-  DexFileFromMemory() = default;
-  virtual ~DexFileFromMemory() = default;
-
-  bool Open(uint64_t dex_file_offset_in_memory, Memory* memory);
+  static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
+                                                   Memory* memory, const std::string& name);
 
  private:
+  DexFileFromMemory(art_api::dex::DexFile&& art_dex_file, std::vector<uint8_t>&& memory)
+      : DexFile(std::move(art_dex_file)), memory_(std::move(memory)) {}
+
   std::vector<uint8_t> memory_;
 };
 
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 451a0b9..63a77e5 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -48,11 +48,7 @@
 DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
     : Global(memory, search_libs) {}
 
-DexFiles::~DexFiles() {
-  for (auto& entry : files_) {
-    delete entry.second;
-  }
-}
+DexFiles::~DexFiles() {}
 
 void DexFiles::ProcessArch() {
   switch (arch()) {
@@ -137,10 +133,11 @@
   DexFile* dex_file;
   auto entry = files_.find(dex_file_offset);
   if (entry == files_.end()) {
-    dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
-    files_[dex_file_offset] = dex_file;
+    std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
+    dex_file = new_dex_file.get();
+    files_[dex_file_offset] = std::move(new_dex_file);
   } else {
-    dex_file = entry->second;
+    dex_file = entry->second.get();
   }
   return dex_file;
 }
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 4ba8a4b..c128b9b 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -204,7 +204,7 @@
 bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
                                            uint64_t* cur_pc) {
   const auto* cfa = &DwarfCfaInfo::kTable[op];
-  if (cfa->name == nullptr) {
+  if (cfa->name[0] == '\0') {
     log(indent, "Illegal");
     log(indent, "Raw Data: 0x%02x", op);
     return true;
@@ -677,29 +677,29 @@
         {DW_EH_PE_uleb128, DW_EH_PE_block},
         {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
     },
-    {nullptr, 0, 0, {}, {}},  // 0x17 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x18 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x19 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1c DW_CFA_lo_user (Treat as illegal)
-    {nullptr, 0, 0, {}, {}},  // 0x1d illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1e illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x1f illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x20 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x21 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x22 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x23 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x24 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x25 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x26 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x27 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x28 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x29 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2c illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x17 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x18 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x19 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1c DW_CFA_lo_user (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x1d illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1e illegal cfa
+    {"", 0, 0, {}, {}},  // 0x1f illegal cfa
+    {"", 0, 0, {}, {}},  // 0x20 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x21 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x22 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x23 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x24 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x25 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x26 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x27 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x28 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x29 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2c illegal cfa
+    {"", 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
     {
         "DW_CFA_GNU_args_size",  // 0x2e DW_CFA_GNU_args_size
         2,
@@ -714,21 +714,21 @@
         {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
         {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
     },
-    {nullptr, 0, 0, {}, {}},  // 0x31 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x32 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x33 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x34 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x35 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x36 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x37 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x38 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x39 illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3a illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3b illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3c illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3d illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3e illegal cfa
-    {nullptr, 0, 0, {}, {}},  // 0x3f DW_CFA_hi_user (Treat as illegal)
+    {"", 0, 0, {}, {}},  // 0x31 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x32 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x33 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x34 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x35 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x36 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x37 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x38 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x39 illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3a illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3b illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3c illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3d illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3e illegal cfa
+    {"", 0, 0, {}, {}},  // 0x3f DW_CFA_hi_user (Treat as illegal)
 };
 
 // Explicitly instantiate DwarfCfa.
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index c5ffb8e..569c17c 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -49,7 +49,14 @@
   };
 
   struct Info {
-    const char* name;
+    // It may seem cleaner to just change the type of 'name' to 'const char *'.
+    // However, having a pointer here would require relocation at runtime,
+    // causing 'kTable' to be placed in data.rel.ro section instead of rodata
+    // section, adding memory pressure to the system.  Note that this is only
+    // safe because this is only used in C++ code.  C++ standard, unlike C
+    // standard, mandates the array size to be large enough to hold the NULL
+    // terminator when initialized with a string literal.
+    const char name[36];
     uint8_t supported_version;
     uint8_t num_operands;
     uint8_t operands[2];
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index c202a33..0336173 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -66,7 +66,7 @@
 
   std::mutex lock_;
   bool initialized_ = false;
-  std::unordered_map<uint64_t, DexFile*> files_;
+  std::unordered_map<uint64_t, std::unique_ptr<DexFile>> files_;
 
   uint64_t entry_addr_ = 0;
   uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 95d2176..21ca47b 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -21,44 +21,37 @@
 #include <unordered_map>
 
 #include <android-base/file.h>
-
+#include <dex/dex_file.h>
+#include <gtest/gtest.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 
-#include <dex/code_item_accessors-inl.h>
-#include <dex/standard_dex_file.h>
-
-#include <gtest/gtest.h>
-
 #include "DexFile.h"
-
 #include "DexFileData.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
 
 TEST(DexFileTest, from_file_open_non_exist) {
-  DexFileFromFile dex_file;
-  ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
+  EXPECT_TRUE(DexFileFromFile::Create(0, "/file/does/not/exist") == nullptr);
 }
 
 TEST(DexFileTest, from_file_open_too_small) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
 
-  ASSERT_EQ(sizeof(art::DexFile::Header) - 2,
+  ASSERT_EQ(sizeof(art::DexFile::Header) - 1,
             static_cast<size_t>(
-                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2)));
+                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header) - 1))));
 
   // Header too small.
-  DexFileFromFile dex_file;
-  ASSERT_FALSE(dex_file.Open(0, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 
   // Header correct, file too small.
   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
   ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
                                               tf.fd, kDexData, sizeof(art::DexFile::Header)))));
-  ASSERT_FALSE(dex_file.Open(0, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 }
 
 TEST(DexFileTest, from_file_open) {
@@ -68,8 +61,7 @@
   ASSERT_EQ(sizeof(kDexData),
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
-  DexFileFromFile dex_file;
-  ASSERT_TRUE(dex_file.Open(0, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) != nullptr);
 }
 
 TEST(DexFileTest, from_file_open_non_zero_offset) {
@@ -80,35 +72,31 @@
   ASSERT_EQ(sizeof(kDexData),
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
-  DexFileFromFile dex_file;
-  ASSERT_TRUE(dex_file.Open(0x100, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0x100, tf.path) != nullptr);
 }
 
 TEST(DexFileTest, from_memory_fail_too_small_for_header) {
   MemoryFake memory;
 
   memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
-  DexFileFromMemory dex_file;
 
-  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
 
 TEST(DexFileTest, from_memory_fail_too_small_for_data) {
   MemoryFake memory;
 
   memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
-  DexFileFromMemory dex_file;
 
-  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
 
 TEST(DexFileTest, from_memory_open) {
   MemoryFake memory;
 
   memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
-  DexFileFromMemory dex_file;
 
-  ASSERT_TRUE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
 }
 
 TEST(DexFileTest, create_using_file) {
@@ -121,8 +109,7 @@
 
   MemoryFake memory;
   MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
-  ASSERT_TRUE(dex_file != nullptr);
+  EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_start) {
@@ -135,8 +122,7 @@
 
   MemoryFake memory;
   MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
-  ASSERT_TRUE(dex_file != nullptr);
+  EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_offset) {
@@ -149,24 +135,21 @@
 
   MemoryFake memory;
   MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
-  ASSERT_TRUE(dex_file != nullptr);
+  EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_empty_file) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
   MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
-  ASSERT_TRUE(dex_file != nullptr);
+  EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
   MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
-  ASSERT_TRUE(dex_file != nullptr);
+  EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_is_malformed) {
@@ -179,22 +162,13 @@
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
   MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+  std::unique_ptr<DexFile> dex_file = DexFile::Create(0x4000, &memory, &info);
   ASSERT_TRUE(dex_file != nullptr);
 
   // Check it came from memory by clearing memory and verifying it fails.
   memory.Clear();
-  dex_file.reset(DexFile::Create(0x4000, &memory, &info));
-  ASSERT_TRUE(dex_file == nullptr);
-}
-
-TEST(DexFileTest, get_method_not_opened) {
-  std::string method("something");
-  uint64_t method_offset = 100;
-  DexFile dex_file;
-  dex_file.GetMethodInformation(0x100, &method, &method_offset);
-  EXPECT_EQ("something", method);
-  EXPECT_EQ(100U, method_offset);
+  dex_file = DexFile::Create(0x4000, &memory, &info);
+  EXPECT_TRUE(dex_file == nullptr);
 }
 
 TEST(DexFileTest, get_method) {
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 600c91c..3e8417e 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -155,7 +155,7 @@
             ],
         },
         linux: {
-            shared_libs: ["libbase"],
+            header_libs: ["libbase_headers"],
             srcs: [
                 "Looper.cpp",
             ],
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 56004fe..7a4a345 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -123,13 +123,14 @@
         if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
             logStackInternal(logtag, stack, priority);
         } else {
-            ALOGW("CallStack::logStackInternal not linked");
+            ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
         }
     }
 
 #else
-    static void ALWAYS_INLINE logStack(const char*, CallStack* = getCurrent().get(),
+    static void ALWAYS_INLINE logStack(const char* logtag, CallStack* = getCurrent().get(),
                                        android_LogPriority = ANDROID_LOG_DEBUG) {
+        ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
     }
 #endif // !WEAKS_AVAILABLE
 
@@ -139,13 +140,13 @@
         if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
             return stackToStringInternal(prefix, stack);
         } else {
-            return String8("<CallStack package not linked>");
+            return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
         }
     }
 #else // !WEAKS_AVAILABLE
-    static String8 ALWAYS_INLINE stackToString(const char* = nullptr,
+    static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
                                                const CallStack* = getCurrent().get()) {
-        return String8("<CallStack package not linked>");
+        return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
     }
 #endif // !WEAKS_AVAILABLE
 
diff --git a/llkd/README.md b/llkd/README.md
index 224e184..191f988 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -86,7 +86,13 @@
 Android Properties
 ------------------
 
-Android Properties llkd respond to (*prop*_ms parms are in milliseconds):
+The following are the Android Properties llkd respond to.
+*prop*_ms named properties are in milliseconds.
+Properties that use comma (*,*) separator for lists, use a leading separator to
+preserve default and add or subtract entries with (*optional*) plus (*+*) and
+minus (*-*) prefixes respectively.
+For these lists, the string "*false*" is synonymous with an *empty* list,
+and *blank* or *missing* resorts to the specified *default* value.
 
 #### ro.config.low_ram
 device is configured with limited memory.
@@ -135,8 +141,8 @@
 default 2 minutes samples of threads for D or Z.
 
 #### ro.llk.stack
-default cma_alloc,__get_user_pages,bit_wait_io comma separated list of kernel
-symbols.  The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
+comma separated list of kernel symbols.
 Look for kernel stack symbols that if ever persistently present can
 indicate a subsystem is locked up.
 Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -153,26 +159,27 @@
 default 0,1,2 (kernel, init and [kthreadd]) plus process names
 init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
 [watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
-The string "*false*" is the equivalent to an *empty* list.
 Do not watch these processes.  A process can be comm, cmdline or pid reference.
 NB: automated default here can be larger than the current maximum property
 size of 92.
 NB: false is a very very very unlikely process to want to blacklist.
 
 #### ro.llk.blacklist.parent
-default 0,2,adbd (kernel, [kthreadd] and adbd).
-The string "*false*" is the equivalent to an *empty* list.
+default 0,2,adbd&[setsid] (kernel, [kthreadd] and adbd *only for zombie setsid*).
 Do not watch processes that have this parent.
-A parent process can be comm, cmdline or pid reference.
+An ampersand (*&*) separator is used to specify that the parent is ignored
+only in combination with the target child process.
+Ampersand was selected because it is never part of a process name,
+however a setprop in the shell requires it to be escaped or quoted;
+init rc file where this is normally specified does not have this issue.
+A parent or target processes can be specified as comm, cmdline or pid reference.
 
 #### ro.llk.blacklist.uid
 default *empty* or false, comma separated list of uid numbers or names.
-The string "*false*" is the equivalent to an *empty* list.
 Do not watch processes that match this uid.
 
 #### ro.llk.blacklist.process.stack
 default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
-The string "*false*" is the equivalent to an *empty* list.
 This subset of processes are not monitored for live lock stack signatures.
 Also prevents the sepolicy violation associated with processes that block
 ptrace, as these can not be checked anyways.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 1efa32b..3586ca1 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -50,16 +50,13 @@
 /* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
 #define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
 #define LLK_CHECK_STACK_PROPERTY       "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages,bit_wait_io"
+#define LLK_CHECK_STACK_DEFAULT        \
+    "cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable"
 #define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
     "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
 #define LLK_BLACKLIST_PARENT_PROPERTY  "ro.llk.blacklist.parent"
-#ifdef __PTRACE_ENABLED__  // defined if userdebug build
-#define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd],adbd"
-#else
-#define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd]"
-#endif
+#define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd],adbd&[setsid]"
 #define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
 #define LLK_BLACKLIST_UID_DEFAULT      ""
 #define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 267da4a..3c295b5 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -24,6 +24,7 @@
 #include <pwd.h>  // getpwuid()
 #include <signal.h>
 #include <stdint.h>
+#include <string.h>
 #include <sys/cdefs.h>  // ___STRING, __predict_true() and _predict_false()
 #include <sys/mman.h>   // mlockall()
 #include <sys/prctl.h>
@@ -107,6 +108,9 @@
 // list of parent pids, comm or cmdline names to skip. default:
 // kernel pid (0), [kthreadd] (2), or ourselves, enforced and implied
 std::unordered_set<std::string> llkBlacklistParent;
+// list of parent and target processes to skip. default:
+// adbd *and* [setsid]
+std::unordered_map<std::string, std::unordered_set<std::string>> llkBlacklistParentAndChild;
 // list of uids, and uid names, to skip, default nothing
 std::unordered_set<std::string> llkBlacklistUid;
 #ifdef __PTRACE_ENABLED__
@@ -617,17 +621,37 @@
 std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
     std::string ret;
     for (const auto& entry : blacklist) {
-        if (ret.size()) {
-            ret += ",";
-        }
+        if (!ret.empty()) ret += ",";
         ret += entry;
     }
     return ret;
 }
 
+std::string llkFormat(
+        const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist,
+        bool leading_comma = false) {
+    std::string ret;
+    for (const auto& entry : blacklist) {
+        for (const auto& target : entry.second) {
+            if (leading_comma || !ret.empty()) ret += ",";
+            ret += entry.first + "&" + target;
+        }
+    }
+    return ret;
+}
+
+// This function parses the properties as a list, incorporating the supplied
+// default.  A leading comma separator means preserve the defaults and add
+// entries (with an optional leading + sign), or removes entries with a leading
+// - sign.
+//
 // We only officially support comma separators, but wetware being what they
 // are will take some liberty and I do not believe they should be punished.
-std::unordered_set<std::string> llkSplit(const std::string& s) {
+std::unordered_set<std::string> llkSplit(const std::string& prop, const std::string& def) {
+    auto s = android::base::GetProperty(prop, def);
+    constexpr char separators[] = ", \t:;";
+    if (!s.empty() && (s != def) && strchr(separators, s[0])) s = def + s;
+
     std::unordered_set<std::string> result;
 
     // Special case, allow boolean false to empty the list, otherwise expected
@@ -637,9 +661,29 @@
 
     size_t base = 0;
     while (s.size() > base) {
-        auto found = s.find_first_of(", \t:", base);
-        // Only emplace content, empty entries are not an option
-        if (found != base) result.emplace(s.substr(base, found - base));
+        auto found = s.find_first_of(separators, base);
+        // Only emplace unique content, empty entries are not an option
+        if (found != base) {
+            switch (s[base]) {
+                case '-':
+                    ++base;
+                    if (base >= s.size()) break;
+                    if (base != found) {
+                        auto have = result.find(s.substr(base, found - base));
+                        if (have != result.end()) result.erase(have);
+                    }
+                    break;
+                case '+':
+                    ++base;
+                    if (base >= s.size()) break;
+                    if (base == found) break;
+                    // FALLTHRU (for gcc, lint, pcc, etc; following for clang)
+                    FALLTHROUGH_INTENDED;
+                default:
+                    result.emplace(s.substr(base, found - base));
+                    break;
+            }
+        }
         if (found == s.npos) break;
         base = found + 1;
     }
@@ -648,13 +692,42 @@
 
 bool llkSkipName(const std::string& name,
                  const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
-    if ((name.size() == 0) || (blacklist.size() == 0)) {
-        return false;
-    }
+    if (name.empty() || blacklist.empty()) return false;
 
     return blacklist.find(name) != blacklist.end();
 }
 
+bool llkSkipProc(proc* procp,
+                 const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
+    if (!procp) return false;
+    if (llkSkipName(std::to_string(procp->pid), blacklist)) return true;
+    if (llkSkipName(procp->getComm(), blacklist)) return true;
+    if (llkSkipName(procp->getCmdline(), blacklist)) return true;
+    if (llkSkipName(android::base::Basename(procp->getCmdline()), blacklist)) return true;
+    return false;
+}
+
+const std::unordered_set<std::string>& llkSkipName(
+        const std::string& name,
+        const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist) {
+    static const std::unordered_set<std::string> empty;
+    if (name.empty() || blacklist.empty()) return empty;
+    auto found = blacklist.find(name);
+    if (found == blacklist.end()) return empty;
+    return found->second;
+}
+
+bool llkSkipPproc(proc* pprocp, proc* procp,
+                  const std::unordered_map<std::string, std::unordered_set<std::string>>&
+                          blacklist = llkBlacklistParentAndChild) {
+    if (!pprocp || !procp || blacklist.empty()) return false;
+    if (llkSkipProc(procp, llkSkipName(std::to_string(pprocp->pid), blacklist))) return true;
+    if (llkSkipProc(procp, llkSkipName(pprocp->getComm(), blacklist))) return true;
+    if (llkSkipProc(procp, llkSkipName(pprocp->getCmdline(), blacklist))) return true;
+    return llkSkipProc(procp,
+                       llkSkipName(android::base::Basename(pprocp->getCmdline()), blacklist));
+}
+
 bool llkSkipPid(pid_t pid) {
     return llkSkipName(std::to_string(pid), llkBlacklistProcess);
 }
@@ -730,26 +803,24 @@
     }
 
     // Don't check process that are known to block ptrace, save sepolicy noise.
-    if (llkSkipName(std::to_string(procp->pid), llkBlacklistStack)) return false;
-    if (llkSkipName(procp->getComm(), llkBlacklistStack)) return false;
-    if (llkSkipName(procp->getCmdline(), llkBlacklistStack)) return false;
-    if (llkSkipName(android::base::Basename(procp->getCmdline()), llkBlacklistStack)) return false;
-
+    if (llkSkipProc(procp, llkBlacklistStack)) return false;
     auto kernel_stack = ReadFile(piddir + "/stack");
     if (kernel_stack.empty()) {
-        LOG(INFO) << piddir << "/stack empty comm=" << procp->getComm()
-                  << " cmdline=" << procp->getCmdline();
+        LOG(VERBOSE) << piddir << "/stack empty comm=" << procp->getComm()
+                     << " cmdline=" << procp->getCmdline();
         return false;
     }
     // A scheduling incident that should not reset count_stack
     if (kernel_stack.find(" cpu_worker_pools+0x") != std::string::npos) return false;
     char idx = -1;
     char match = -1;
+    std::string matched_stack_symbol = "<unknown>";
     for (const auto& stack : llkCheckStackSymbols) {
         if (++idx < 0) break;
         if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
             (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
             match = idx;
+            matched_stack_symbol = stack;
             break;
         }
     }
@@ -760,7 +831,9 @@
     }
     if (match == char(-1)) return false;
     procp->count_stack += llkCycle;
-    return procp->count_stack >= llkStateTimeoutMs[llkStateStack];
+    if (procp->count_stack < llkStateTimeoutMs[llkStateStack]) return false;
+    LOG(WARNING) << "Found " << matched_stack_symbol << " in stack for pid " << procp->pid;
+    return true;
 }
 #endif
 
@@ -776,12 +849,12 @@
     // but if there are problems we assume at least a few
     // samples of reads occur before we take any real action.
     std::string schedString = ReadFile(piddir + "/sched");
-    if (schedString.size() == 0) {
+    if (schedString.empty()) {
         // /schedstat is not as standardized, but in 3.1+
         // Android devices, the third field is nr_switches
         // from /sched:
         schedString = ReadFile(piddir + "/schedstat");
-        if (schedString.size() == 0) {
+        if (schedString.empty()) {
             return;
         }
         auto val = static_cast<unsigned long long>(-1);
@@ -839,7 +912,8 @@
               << LLK_BLACKLIST_STACK_PROPERTY "=" << llkFormat(llkBlacklistStack) << "\n"
 #endif
               << LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
-              << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent) << "\n"
+              << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent)
+              << llkFormat(llkBlacklistParentAndChild, true) << "\n"
               << LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
 }
 
@@ -939,7 +1013,7 @@
 
             // Get the process stat
             std::string stat = ReadFile(piddir + "/stat");
-            if (stat.size() == 0) {
+            if (stat.empty()) {
                 continue;
             }
             unsigned tid = -1;
@@ -1014,7 +1088,8 @@
                 break;
             }
 
-            if (llkSkipName(procp->getComm())) {
+            auto process_comm = procp->getComm();
+            if (llkSkipName(process_comm)) {
                 continue;
             }
             if (llkSkipName(procp->getCmdline())) {
@@ -1028,11 +1103,11 @@
             if (pprocp == nullptr) {
                 pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
             }
-            if ((pprocp != nullptr) &&
-                (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
-                 llkSkipName(pprocp->getCmdline(), llkBlacklistParent) ||
-                 llkSkipName(android::base::Basename(pprocp->getCmdline()), llkBlacklistParent))) {
-                break;
+            if (pprocp) {
+                if (llkSkipPproc(pprocp, procp)) break;
+                if (llkSkipProc(pprocp, llkBlacklistParent)) break;
+            } else {
+                if (llkSkipName(std::to_string(ppid), llkBlacklistParent)) break;
             }
 
             if ((llkBlacklistUid.size() != 0) && llkSkipUid(procp->getUid())) {
@@ -1049,7 +1124,7 @@
                     stuck = true;
                 } else if (procp->count != 0ms) {
                     LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
-                                 << pid << "->" << tid << ' ' << procp->getComm();
+                                 << pid << "->" << tid << ' ' << process_comm;
                 }
             }
             if (!stuck) continue;
@@ -1057,7 +1132,7 @@
             if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
                 if (procp->count != 0ms) {
                     LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
-                                 << pid << "->" << tid << ' ' << procp->getComm();
+                                 << pid << "->" << tid << ' ' << process_comm;
                 }
                 continue;
             }
@@ -1085,7 +1160,7 @@
                             break;
                         }
                         LOG(WARNING) << "Z " << llkFormat(procp->count) << ' ' << ppid << "->"
-                                     << pid << "->" << tid << ' ' << procp->getComm() << " [kill]";
+                                     << pid << "->" << tid << ' ' << process_comm << " [kill]";
                         if ((llkKillOneProcess(pprocp, procp) >= 0) ||
                             (llkKillOneProcess(ppid, procp) >= 0)) {
                             continue;
@@ -1102,7 +1177,7 @@
                         // kernel (worse).
                     default:
                         LOG(WARNING) << state << ' ' << llkFormat(procp->count) << ' ' << pid
-                                     << "->" << tid << ' ' << procp->getComm() << " [kill]";
+                                     << "->" << tid << ' ' << process_comm << " [kill]";
                         if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||
                             (llkKillOneProcess(pid, state, tid) >= 0) ||
                             (llkKillOneProcess(procp, procp) >= 0) ||
@@ -1115,7 +1190,7 @@
             // We are here because we have confirmed kernel live-lock
             const auto message = state + " "s + llkFormat(procp->count) + " " +
                                  std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
-                                 std::to_string(tid) + " " + procp->getComm() + " [panic]";
+                                 std::to_string(tid) + " " + process_comm + " [panic]";
             llkPanicKernel(dump, tid,
                            (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
                            message);
@@ -1131,21 +1206,15 @@
         if (!p->second.updated) {
             IF_ALOG(LOG_VERBOSE, LOG_TAG) {
                 std::string ppidCmdline = llkProcGetName(p->second.ppid, nullptr, nullptr);
-                if (ppidCmdline.size()) {
-                    ppidCmdline = "(" + ppidCmdline + ")";
-                }
+                if (!ppidCmdline.empty()) ppidCmdline = "(" + ppidCmdline + ")";
                 std::string pidCmdline;
                 if (p->second.pid != p->second.tid) {
                     pidCmdline = llkProcGetName(p->second.pid, nullptr, p->second.getCmdline());
-                    if (pidCmdline.size()) {
-                        pidCmdline = "(" + pidCmdline + ")";
-                    }
+                    if (!pidCmdline.empty()) pidCmdline = "(" + pidCmdline + ")";
                 }
                 std::string tidCmdline =
                     llkProcGetName(p->second.tid, p->second.getComm(), p->second.getCmdline());
-                if (tidCmdline.size()) {
-                    tidCmdline = "(" + tidCmdline + ")";
-                }
+                if (!tidCmdline.empty()) tidCmdline = "(" + tidCmdline + ")";
                 LOG(VERBOSE) << "thread " << p->second.ppid << ppidCmdline << "->" << p->second.pid
                              << pidCmdline << "->" << p->second.tid << tidCmdline << " removed";
             }
@@ -1222,13 +1291,11 @@
     llkValidate();  // validate all (effectively minus llkTimeoutMs)
 #ifdef __PTRACE_ENABLED__
     if (debuggable) {
-        llkCheckStackSymbols = llkSplit(
-                android::base::GetProperty(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT));
+        llkCheckStackSymbols = llkSplit(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT);
     }
     std::string defaultBlacklistStack(LLK_BLACKLIST_STACK_DEFAULT);
     if (!debuggable) defaultBlacklistStack += ",logd,/system/bin/logd";
-    llkBlacklistStack = llkSplit(
-            android::base::GetProperty(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack));
+    llkBlacklistStack = llkSplit(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack);
 #endif
     std::string defaultBlacklistProcess(
         std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
@@ -1240,17 +1307,34 @@
     for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
         defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
     }
-    defaultBlacklistProcess =
-        android::base::GetProperty(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
-    llkBlacklistProcess = llkSplit(defaultBlacklistProcess);
+    llkBlacklistProcess = llkSplit(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
     if (!llkSkipName("[khungtaskd]")) {  // ALWAYS ignore as special
         llkBlacklistProcess.emplace("[khungtaskd]");
     }
-    llkBlacklistParent = llkSplit(android::base::GetProperty(
-        LLK_BLACKLIST_PARENT_PROPERTY, std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
-                                           "," LLK_BLACKLIST_PARENT_DEFAULT));
-    llkBlacklistUid =
-        llkSplit(android::base::GetProperty(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT));
+    llkBlacklistParent = llkSplit(LLK_BLACKLIST_PARENT_PROPERTY,
+                                  std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
+                                          "," LLK_BLACKLIST_PARENT_DEFAULT);
+    // derive llkBlacklistParentAndChild by moving entries with '&' from above
+    for (auto it = llkBlacklistParent.begin(); it != llkBlacklistParent.end();) {
+        auto pos = it->find('&');
+        if (pos == std::string::npos) {
+            ++it;
+            continue;
+        }
+        auto parent = it->substr(0, pos);
+        auto child = it->substr(pos + 1);
+        it = llkBlacklistParent.erase(it);
+
+        auto found = llkBlacklistParentAndChild.find(parent);
+        if (found == llkBlacklistParentAndChild.end()) {
+            llkBlacklistParentAndChild.emplace(std::make_pair(
+                    std::move(parent), std::unordered_set<std::string>({std::move(child)})));
+        } else {
+            found->second.emplace(std::move(child));
+        }
+    }
+
+    llkBlacklistUid = llkSplit(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT);
 
     // internal watchdog
     ::signal(SIGALRM, llkAlarmHandler);
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index f54932b..96079cc 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -17,6 +17,7 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <stdint.h>
+#include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -87,7 +88,8 @@
         execute("stop llkd-1");
         rest();
         std::string setprop("setprop ");
-        execute((setprop + LLK_CHECK_STACK_PROPERTY + " SyS_openat").c_str());
+        // Manually check that SyS_openat is _added_ to the list when restarted
+        execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat").c_str());
         rest();
         execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
         rest();
@@ -332,3 +334,37 @@
 
     unlink(stack_pipe_file);
 }
+
+// b/120983740
+TEST(llkd, adbd_and_setsid) {
+    if (checkKill("kernel_panic,sysrq,livelock,zombie")) {
+        return;
+    }
+    const auto period = llkdSleepPeriod('S');
+
+    // expect llkd.zombie to trigger, but not for adbd&[setsid]
+    // Create a Persistent Zombie setsid Process
+    pid_t child_pid = fork();
+    ASSERT_LE(0, child_pid);
+    if (!child_pid) {
+        prctl(PR_SET_NAME, "adbd");
+        auto zombie_pid = fork();
+        ASSERT_LE(0, zombie_pid);
+        if (!zombie_pid) {
+            prctl(PR_SET_NAME, "setsid");
+            sleep(1);
+            exit(0);
+        }
+        sleep(period.count());
+        exit(42);
+    }
+
+    // Reverse of waitForPid, do _not_ expect kill
+    int wstatus;
+    ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+    EXPECT_TRUE(WIFEXITED(wstatus));
+    if (WIFEXITED(wstatus)) {
+        EXPECT_EQ(42, WEXITSTATUS(wstatus));
+    }
+    ASSERT_FALSE(WIFSIGNALED(wstatus)) << "[   INFO   ] signo=" << WTERMSIG(wstatus);
+}
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 19e6d68..2fd9f95 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -55,8 +55,19 @@
       mMsgLen(elem.mMsgLen),
       mLogId(elem.mLogId),
       mDropped(elem.mDropped) {
-    mMsg = new char[mMsgLen];
-    memcpy(mMsg, elem.mMsg, mMsgLen);
+    if (mDropped) {
+        if (elem.isBinary() && elem.mMsg != nullptr) {
+            // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
+            const int len = sizeof(android_event_header_t);
+            mMsg = new char[len];
+            memcpy(mMsg, elem.mMsg, len);
+        } else {
+            mMsg = nullptr;
+        }
+    } else {
+        mMsg = new char[mMsgLen];
+        memcpy(mMsg, elem.mMsg, mMsgLen);
+    }
 }
 
 LogBufferElement::~LogBufferElement() {
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index f4e165f..9f6f203 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -18,6 +18,7 @@
 #define _LOGD_LOG_TIMES_H__
 
 #include <pthread.h>
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <time.h>
 
@@ -82,6 +83,8 @@
     void cleanSkip_Locked(void);
 
     void release_Locked(void) {
+        // gracefully shut down the socket.
+        shutdown(mClient->getSocket(), SHUT_RDWR);
         mRelease = true;
         pthread_cond_signal(&threadTriggeredCondition);
     }
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index e510c3c..5601e53 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -105,6 +105,29 @@
 namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
 namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
+namespace.default.asan.permitted.paths += /apex/com.android.resolv/${LIB}
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs  = libc.so:libdl.so:libm.so
+namespace.default.link.runtime.shared_libs += libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
 # "sphal" namespace
@@ -140,8 +163,12 @@
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+namespace.sphal.links = runtime,default,vndk,rs
 
+namespace.sphal.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+# LLNDK_LIBRARIES includes the runtime libs above, but the order here ensures
+# that they are loaded from the runtime namespace.
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -188,9 +215,11 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk
+namespace.rs.links = runtime,default,vndk
 
-namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 # Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
 # namespace because RS framework libs are using them.
@@ -236,10 +265,13 @@
 namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
 namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
-# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
-# "sphal" namespace for vendor libs.  The ordering matters.  The "default"
-# namespace has higher priority than the "sphal" namespace.
-namespace.vndk.links = default,sphal
+# The "vndk" namespace links to "runtime" for Bionic libs, "default" namespace
+# for LLNDK libs, and links to "sphal" namespace for vendor libs. The ordering
+# matters. The "default" namespace has higher priority than the "sphal"
+# namespace.
+namespace.vndk.links = runtime,default,sphal
+
+namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
 
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
@@ -250,6 +282,7 @@
 # Allow VNDK-SP extensions to use vendor libraries
 namespace.vndk.link.sphal.allow_all_shared_libs = true
 
+
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
 # them. However, in O-MR1, access to /system/${LIB} will not be allowed to
@@ -257,7 +290,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = system,vndk
+additional.namespaces = runtime,system,vndk
 
 ###############################################################################
 # "default" namespace
@@ -288,12 +321,24 @@
 namespace.default.asan.permitted.paths += /data/asan/vendor
 namespace.default.asan.permitted.paths +=           /vendor
 
-namespace.default.links = system,vndk
+namespace.default.links = runtime,system,vndk
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
 namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
 namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
 namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
 
 ###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = system
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.system.allow_all_shared_libs = true
+
+###############################################################################
 # "vndk" namespace
 #
 # This namespace is where VNDK and VNDK-SP libraries are loaded for
@@ -324,7 +369,10 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the system namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = system,default
+namespace.vndk.links = runtime,system,default
+
+namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
 namespace.vndk.link.system.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -349,16 +397,36 @@
 namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
+namespace.system.links = runtime
+namespace.system.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+
 ###############################################################################
 # Namespace config for binaries under /postinstall.
-# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# Only default and runtime namespaces are defined and default has no directories
+# other than /system/lib in the search paths. This is because linker calls
+# realpath on the search paths and this causes selinux denial if the paths
+# (/vendor, /odm) are not allowed to the postinstall binaries. There is no
+# reason to allow the binaries to access the paths.
 ###############################################################################
 [postinstall]
+additional.namespaces = runtime
+
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.default.allow_all_shared_libs = true
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 7e354ac..7ca45ff 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -55,6 +55,27 @@
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs  = libc.so:libdl.so:libm.so
+namespace.default.link.runtime.shared_libs += libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
 ###############################################################################
 # "sphal" namespace
 #
@@ -89,8 +110,12 @@
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+namespace.sphal.links = runtime,default,vndk,rs
 
+namespace.sphal.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+# LLNDK_LIBRARIES includes the runtime libs above, but the order here ensures
+# that they are loaded from the runtime namespace.
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -137,9 +162,11 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk
+namespace.rs.links = runtime,default,vndk
 
-namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 # Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
 # namespace because RS framework libs are using them.
@@ -188,10 +215,14 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = default
+namespace.vndk.links = runtime,default
+
+namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
 namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
+
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
 # them. However, in O-MR1, access to /system/${LIB} will not be allowed to
@@ -199,6 +230,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
+additional.namespaces = runtime
 namespace.default.isolated = false
 
 namespace.default.search.paths  = /odm/${LIB}
@@ -208,7 +240,7 @@
 namespace.default.search.paths += /vendor/${LIB}/vndk
 namespace.default.search.paths += /vendor/${LIB}/vndk-sp
 
-# Access to system libraries are allowed
+# Access to system libraries is allowed
 namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}
@@ -238,16 +270,47 @@
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+
 ###############################################################################
 # Namespace config for binaries under /postinstall.
-# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# Only default and runtime namespaces are defined and default has no directories
+# other than /system/lib in the search paths. This is because linker calls
+# realpath on the search paths and this causes selinux denial if the paths
+# (/vendor, /odm) are not allowed to the postinstall binaries. There is no
+# reason to allow the binaries to access the paths.
 ###############################################################################
 [postinstall]
+additional.namespaces = runtime
+
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
+namespace.runtime.link.default.allow_all_shared_libs = true
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6fb1a8b..0ec6e17 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -544,7 +544,8 @@
 
     mkdir /data/anr 0775 system system
 
-    mkdir /data/apex 0770 root root
+    mkdir /data/apex 0750 root system
+    mkdir /data/staging 0750 system system
 
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 35f469a..451f5ad 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -44,8 +44,6 @@
 /dev/dri/*                0666   root       graphics
 
 # these should not be world writable
-/dev/diag                 0660   radio      radio
-/dev/ttyMSM0              0600   bluetooth  bluetooth
 /dev/uhid                 0660   uhid       uhid
 /dev/uinput               0660   uhid       uhid
 /dev/rtc0                 0640   system     system
@@ -54,7 +52,6 @@
 /dev/input/*              0660   root       input
 /dev/v4l-touch*           0660   root       input
 /dev/snd/*                0660   system     audio
-/dev/msm_mp3*             0660   system     audio
 /dev/bus/usb/*            0660   root       usb
 /dev/mtp_usb              0660   root       mtp
 /dev/usb_accessory        0660   root       usb
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 56a30b2..79bed7b 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -38,9 +38,11 @@
 
 sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
   $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
   $(UBSAN_RUNTIME_LIBRARY) \
   $(TSAN_RUNTIME_LIBRARY) \
   $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
   $(2ND_UBSAN_RUNTIME_LIBRARY) \
   $(2ND_TSAN_RUNTIME_LIBRARY)))
 # If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.
diff --git a/trusty/utils/trusty-ut-ctrl/Android.bp b/trusty/utils/trusty-ut-ctrl/Android.bp
new file mode 100644
index 0000000..77d1f70
--- /dev/null
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2018 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_test {
+    name: "trusty-ut-ctrl",
+    vendor: true,
+
+    srcs: ["ut-ctrl.c"],
+    shared_libs: [
+        "libc",
+        "liblog",
+        "libtrusty",
+    ],
+    gtest: false,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/trusty/utils/trusty-ut-ctrl/ut-ctrl.c b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
new file mode 100644
index 0000000..9e72af3
--- /dev/null
+++ b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 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 <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <trusty/tipc.h>
+
+#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
+
+static const char* dev_name = NULL;
+static const char* ut_app = NULL;
+
+static const char* _sopts = "hD:";
+static const struct option _lopts[] = {
+        {"help", no_argument, 0, 'h'},
+        {"dev", required_argument, 0, 'D'},
+        {0, 0, 0, 0},
+};
+
+static const char* usage =
+        "Usage: %s [options] unittest-app\n"
+        "\n"
+        "options:\n"
+        "  -h, --help            prints this message and exit\n"
+        "  -D, --dev name        Trusty device name\n"
+        "\n";
+
+static const char* usage_long = "\n";
+
+static bool opt_silent = false;
+
+static void print_usage_and_exit(const char* prog, int code, bool verbose) {
+    fprintf(stderr, usage, prog);
+    if (verbose) {
+        fprintf(stderr, "%s", usage_long);
+    }
+    exit(code);
+}
+
+static void parse_options(int argc, char** argv) {
+    int c;
+    int oidx = 0;
+
+    while (1) {
+        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+        if (c == -1) {
+            break; /* done */
+        }
+
+        switch (c) {
+            case 'D':
+                dev_name = strdup(optarg);
+                break;
+
+            case 's':
+                opt_silent = true;
+                break;
+
+            case 'h':
+                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+                break;
+
+            default:
+                print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+        }
+    }
+
+    if (optind < argc) {
+        ut_app = strdup(argv[optind]);
+    }
+}
+
+enum test_message_header {
+    TEST_PASSED = 0,
+    TEST_FAILED = 1,
+    TEST_MESSAGE = 2,
+};
+
+static int run_trusty_unitest(const char* utapp) {
+    int fd;
+    int rc;
+    char rx_buf[1024];
+
+    /* connect to unitest app */
+    fd = tipc_connect(dev_name, utapp);
+    if (fd < 0) {
+        fprintf(stderr, "failed to connect to '%s' app: %s\n", utapp, strerror(-fd));
+        return fd;
+    }
+
+    /* wait for test to complete */
+    for (;;) {
+        rc = read(fd, rx_buf, sizeof(rx_buf));
+        if (rc <= 0 || rc >= (int)sizeof(rx_buf)) {
+            fprintf(stderr, "%s: Read failed: %d\n", __func__, rc);
+            tipc_close(fd);
+            return -1;
+        }
+
+        if (rx_buf[0] == TEST_PASSED) {
+            break;
+        } else if (rx_buf[0] == TEST_FAILED) {
+            break;
+        } else if (rx_buf[0] == TEST_MESSAGE) {
+            write(STDOUT_FILENO, rx_buf + 1, rc - 1);
+        } else {
+            fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
+            break;
+        }
+    }
+
+    /* close connection to unitest app */
+    tipc_close(fd);
+
+    return rx_buf[0] == TEST_PASSED ? 0 : -1;
+}
+
+int main(int argc, char** argv) {
+    int rc = 0;
+
+    if (argc <= 1) {
+        print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+    }
+
+    parse_options(argc, argv);
+
+    if (!dev_name) {
+        dev_name = TIPC_DEFAULT_DEVNAME;
+    }
+
+    if (!ut_app) {
+        fprintf(stderr, "Unittest app must be specified\n");
+        print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+    }
+
+    rc = run_trusty_unitest(ut_app);
+
+    return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}