Merge "health: Add CAP_WAKE_ALARM to service via init"
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.h b/adb/adb.h
index 47ea0e8..d79cd2d 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -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 e6fefda..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>
@@ -182,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/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/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/services.cpp b/adb/daemon/services.cpp
index 3693997..5ae210f 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -328,6 +328,13 @@
 }
 
 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)};
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 595d5c6..455595f 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -141,7 +141,7 @@
 class Subprocess {
   public:
     Subprocess(std::string command, const char* terminal_type, SubprocessType type,
-               SubprocessProtocol protocol);
+               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_;
 
@@ -192,24 +196,12 @@
 };
 
 Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
-                       SubprocessProtocol protocol)
+                       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";
@@ -747,20 +802,49 @@
 
 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.c_str());
 
-    auto subprocess = std::make_unique<Subprocess>(std::move(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());
@@ -769,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 421d61f..fc66377 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -20,6 +20,8 @@
 
 #include "adb_unique_fd.h"
 
+#include <string_view>
+
 enum class SubprocessType {
     kPty,
     kRaw,
@@ -36,3 +38,18 @@
 // Returns an open FD connected to the subprocess or -1 on failure.
 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/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
index f44ff41..32a5d21 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -114,7 +114,7 @@
 
     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"};
+    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;
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 097e07f..4a0d4b5 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -165,23 +165,13 @@
     return true;
 }
 
-static bool GetBlockDeviceParams(int bdev_fd, const std::string& bdev_path, uint64_t* blocksz,
-                                 uint64_t* bdev_size) {
-    // TODO: For some reason, the block device ioctl require the argument to be initialized
-    // to zero even if its the out parameter for the given ioctl cmd.
-    uint64_t blksz = 0;
-    if (ioctl(bdev_fd, BLKBSZGET, &blksz)) {
-        PLOG(ERROR) << "Failed to get block size for: " << bdev_path;
-        return false;
-    }
-
+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;
     }
 
-    *blocksz = blksz;
     *bdev_size = size_in_bytes;
 
     return true;
@@ -197,22 +187,20 @@
     return sb.st_size;
 }
 
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t blocksz,
+static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
                               uint32_t* fs_type) {
-    // Check if the size aligned to the block size of the block device.
-    // We need this to be true in order to be able to write the file using FIEMAP.
-    if (file_size % blocksz) {
-        LOG(ERROR) << "File size " << file_size << " is not aligned to block size " << blocksz
-                   << " for file " << file_path;
-        return false;
-    }
-
     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)) {
@@ -226,6 +214,7 @@
         return false;
     }
 
+    *blocksz = sfs.f_bsize;
     *fs_type = sfs.f_type;
     return true;
 }
@@ -303,7 +292,7 @@
 
     uint32_t pin_status = 1;
     int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
-    if (error) {
+    if (error < 0) {
         if ((errno == ENOTTY) || (errno == ENOTSUP)) {
             PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
         } else {
@@ -316,7 +305,7 @@
 }
 
 #if 0
-static bool PinFileStatus(int file_fd, const std::string& file_path, uint32_t fs_type) {
+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.
@@ -339,9 +328,10 @@
 #define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
 #endif
 
-    uint32_t pin_status;
-    int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &pin_status);
-    if (error) {
+    // 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 {
@@ -350,7 +340,10 @@
         return false;
     }
 
-    return !!pin_status;
+    if (moved_blocks_nr) {
+        LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
+    }
+    return moved_blocks_nr == 0;
 }
 #endif
 
@@ -447,7 +440,7 @@
     std::string bdev_path;
     if (!FileToBlockDevicePath(abs_path, &bdev_path)) {
         LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
-        cleanup(file_path, create);
+        cleanup(abs_path, create);
         return nullptr;
     }
 
@@ -459,9 +452,9 @@
         return nullptr;
     }
 
-    uint64_t blocksz, bdevsz;
-    if (!GetBlockDeviceParams(bdev_fd, bdev_path, &blocksz, &bdevsz)) {
-        LOG(ERROR) << "Failed to get block device params for: " << bdev_path;
+    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;
     }
@@ -474,23 +467,24 @@
         }
     }
 
+    uint64_t blocksz;
     uint32_t fs_type;
-    if (!PerformFileChecks(abs_path, file_size, blocksz, &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(file_path, create);
+        cleanup(abs_path, create);
         return nullptr;
     }
 
     if (create) {
         if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) {
-            unlink(abs_path.c_str());
+            cleanup(abs_path, create);
             return nullptr;
         }
     }
 
     // f2fs may move the file blocks around.
     if (!PinFile(file_fd, file_path, fs_type)) {
-        cleanup(file_path, create);
+        cleanup(abs_path, create);
         LOG(ERROR) << "Failed to pin the file in storage";
         return nullptr;
     }
@@ -499,7 +493,7 @@
     FiemapUniquePtr fmap(new FiemapWriter());
     if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
         LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
-        cleanup(file_path, create);
+        cleanup(abs_path, create);
         return nullptr;
     }
 
@@ -543,9 +537,10 @@
                    << " for block size " << block_size_;
         return false;
     }
+
 #if 0
     // TODO(b/122138114): check why this fails.
-    if (!PinFileStatus(file_fd_, file_path_, fs_type_)) {
+    if (!IsFilePinned(file_fd_, file_path_, fs_type_)) {
         LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
         return false;
     }
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index 6653ada..6dff0e8 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -40,11 +40,22 @@
 using unique_fd = android::base::unique_fd;
 using LoopDevice = android::dm::LoopDevice;
 
-std::string testfile = "";
 std::string testbdev = "";
 uint64_t testfile_size = 536870912;  // default of 512MiB
 
-TEST(FiemapWriter, CreateImpossiblyLargeFile) {
+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.
@@ -54,7 +65,7 @@
     EXPECT_EQ(errno, ENOENT);
 }
 
-TEST(FiemapWriter, CreateUnalignedFile) {
+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.
@@ -64,7 +75,7 @@
     EXPECT_EQ(errno, ENOENT);
 }
 
-TEST(FiemapWriter, CheckFilePath) {
+TEST_F(FiemapWriterTest, CheckFilePath) {
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
     ASSERT_NE(fptr, nullptr);
     EXPECT_EQ(fptr->size(), 4096);
@@ -72,20 +83,20 @@
     EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
 }
 
-TEST(FiemapWriter, CheckBlockDevicePath) {
+TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
     EXPECT_EQ(fptr->size(), 4096);
     EXPECT_EQ(fptr->bdev_path(), testbdev);
 }
 
-TEST(FiemapWriter, CheckFileCreated) {
+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(FiemapWriter, CheckFileSizeActual) {
+TEST_F(FiemapWriterTest, CheckFileSizeActual) {
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
     ASSERT_NE(fptr, nullptr);
 
@@ -94,13 +105,13 @@
     EXPECT_EQ(sb.st_size, testfile_size);
 }
 
-TEST(FiemapWriter, CheckFileExtents) {
+TEST_F(FiemapWriterTest, CheckFileExtents) {
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
     ASSERT_NE(fptr, nullptr);
     EXPECT_GT(fptr->extents().size(), 0);
 }
 
-TEST(FiemapWriter, CheckWriteError) {
+TEST_F(FiemapWriterTest, CheckWriteError) {
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
     ASSERT_NE(fptr, nullptr);
 
@@ -339,18 +350,17 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
-    if (argc <= 2) {
+    if (argc <= 1) {
         cerr << "Filepath with its bdev path must be provided as follows:" << endl;
-        cerr << "  $ fiemap_writer_test <path to file> </dev/block/XXXX" << 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);
 
-    testfile = argv[1];
-    testbdev = argv[2];
-    if (argc > 3) {
-        testfile_size = strtoull(argv[3], NULL, 0);
+    testbdev = argv[1];
+    if (argc > 2) {
+        testfile_size = strtoull(argv[2], NULL, 0);
         if (testfile_size == ULLONG_MAX) {
             testfile_size = 512 * 1024 * 1024;
         }
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-----
+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
+-----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/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/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/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/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 49ae960..5601e53 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -334,11 +334,9 @@
 ###############################################################################
 namespace.runtime.isolated = true
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = system,default
-namespace.runtime.link.system.shared_libs  = %LLNDK_LIBRARIES%
-namespace.runtime.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.runtime.links = system
 # TODO(b/119867084): Restrict to Bionic dlopen dependencies.
-namespace.runtime.link.default.allow_all_shared_libs = true
+namespace.runtime.link.system.allow_all_shared_libs = true
 
 ###############################################################################
 # "vndk" namespace
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/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;
+}