Merge "adbd: switch the default USB implementation."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc85408..716378b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,9 @@
       "name": "fs_mgr_unit_test"
     },
     {
+      "name": "fs_mgr_vendor_overlay_test"
+    },
+    {
       "name": "init_tests"
     },
     {
diff --git a/adb/adb.h b/adb/adb.h
index 9209997..3a8cb7b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -153,7 +153,7 @@
 #endif
 
 #if !ADB_HOST
-unique_fd execute_binder_command(std::string_view command);
+unique_fd execute_abb_command(std::string_view command);
 #endif
 
 #if !ADB_HOST
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 605d27d..91b0d1f 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -187,79 +187,3 @@
         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 2ccaa32..e2df1b1 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -74,13 +74,4 @@
 
 // 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/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index b8827ef..7e470e1 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -681,9 +681,7 @@
     if (sync) {
         struct stat st;
         if (sync_lstat(sc, rpath, &st)) {
-            // For links, we cannot update the atime/mtime.
-            if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
-                (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+            if (st.st_mtime == static_cast<time_t>(mtime)) {
                 sc.RecordFilesSkipped(1);
                 return true;
             }
@@ -921,12 +919,8 @@
         for (copyinfo& ci : file_list) {
             struct stat st;
             if (sc.FinishStat(&st)) {
-                if (st.st_size == static_cast<off_t>(ci.size)) {
-                    // For links, we cannot update the atime/mtime.
-                    if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
-                        (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
-                        ci.skip = true;
-                    }
+                if (st.st_size == static_cast<off_t>(ci.size) && st.st_mtime == ci.time) {
+                    ci.skip = true;
                 }
             }
         }
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index f69babe..4ffa6bb 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -22,6 +22,8 @@
 
 #include <sys/wait.h>
 
+#include <android-base/cmsg.h>
+
 namespace {
 
 class AdbFdTextOutput : public android::TextOutput {
@@ -83,8 +85,20 @@
             break;
         }
 
-        auto result = StartCommandInProcess(std::move(data), &execCmd);
-        if (!SendFileDescriptor(fd, result)) {
+        std::string_view name = data;
+        auto protocol = SubprocessProtocol::kShell;
+        if (name.starts_with("abb:")) {
+            name.remove_prefix(strlen("abb:"));
+            protocol = SubprocessProtocol::kShell;
+        } else if (name.starts_with("abb_exec:")) {
+            name.remove_prefix(strlen("abb_exec:"));
+            protocol = SubprocessProtocol::kNone;
+        } else {
+            LOG(FATAL) << "Unknown command prefix for abb: " << data;
+        }
+
+        unique_fd result = StartCommandInProcess(std::string(name), &execCmd, protocol);
+        if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
             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
index 817aea1..a435279 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -20,6 +20,8 @@
 #include "adb_utils.h"
 #include "shell_service.h"
 
+#include <android-base/cmsg.h>
+
 namespace {
 
 struct AbbProcess;
@@ -59,8 +61,9 @@
 
         unique_fd fd;
         std::string error;
-        if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
-            LOG(ERROR) << "failed to receive FD from abb: " << error;
+        char buf;
+        if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
+            PLOG(ERROR) << "failed to receive FD from abb";
             socket_fd_.reset();
             continue;
         }
@@ -83,6 +86,6 @@
 
 }  // namespace
 
-unique_fd execute_binder_command(std::string_view command) {
+unique_fd execute_abb_command(std::string_view command) {
     return abbp->sendCommand(command);
 }
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 9e1760d..70deb31 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <utime.h>
@@ -242,10 +243,10 @@
     return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
-static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
-                             mode_t mode, std::vector<char>& buffer, bool do_unlink) {
+static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
+                             uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
+                             bool do_unlink) {
     syncmsg msg;
-    unsigned int timestamp = 0;
 
     __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
 
@@ -291,7 +292,7 @@
 
         if (msg.data.id != ID_DATA) {
             if (msg.data.id == ID_DONE) {
-                timestamp = msg.data.size;
+                *timestamp = msg.data.size;
                 break;
             }
             SendSyncFail(s, "invalid data message");
@@ -316,11 +317,6 @@
         goto fail;
     }
 
-    utimbuf u;
-    u.actime = timestamp;
-    u.modtime = timestamp;
-    utime(path, &u);
-
     msg.status.id = ID_OKAY;
     msg.status.msglen = 0;
     return WriteFdExactly(s, &msg.status, sizeof(msg.status));
@@ -360,9 +356,12 @@
 }
 
 #if defined(_WIN32)
-extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
+extern bool handle_send_link(int s, const std::string& path,
+                             uint32_t* timestamp, std::vector<char>& buffer)
+        __attribute__((error("no symlinks on Windows")));
 #else
-static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
+static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp,
+                             std::vector<char>& buffer) {
     syncmsg msg;
 
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -399,6 +398,7 @@
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
     if (msg.data.id == ID_DONE) {
+        *timestamp = msg.data.size;
         msg.status.id = ID_OKAY;
         msg.status.msglen = 0;
         if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
@@ -448,24 +448,40 @@
         adb_unlink(path.c_str());
     }
 
+    bool result;
+    uint32_t timestamp;
     if (S_ISLNK(mode)) {
-        return handle_send_link(s, path.c_str(), buffer);
+        result = handle_send_link(s, path.c_str(), &timestamp, buffer);
+    } else {
+        // Copy user permission bits to "group" and "other" permissions.
+        mode &= 0777;
+        mode |= ((mode >> 3) & 0070);
+        mode |= ((mode >> 3) & 0007);
+
+        uid_t uid = -1;
+        gid_t gid = -1;
+        uint64_t capabilities = 0;
+        if (should_use_fs_config(path)) {
+            unsigned int broken_api_hack = mode;
+            fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
+            mode = broken_api_hack;
+        }
+
+        result = handle_send_file(s, path.c_str(), &timestamp, uid, gid, capabilities, mode, buffer,
+                                  do_unlink);
     }
 
-    // Copy user permission bits to "group" and "other" permissions.
-    mode &= 0777;
-    mode |= ((mode >> 3) & 0070);
-    mode |= ((mode >> 3) & 0007);
-
-    uid_t uid = -1;
-    gid_t gid = -1;
-    uint64_t capabilities = 0;
-    if (should_use_fs_config(path)) {
-        unsigned int broken_api_hack = mode;
-        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
-        mode = broken_api_hack;
+    if (!result) {
+      return false;
     }
-    return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
+
+    struct timeval tv[2];
+    tv[0].tv_sec = timestamp;
+    tv[0].tv_usec = 0;
+    tv[1].tv_sec = timestamp;
+    tv[1].tv_usec = 0;
+    lutimes(path.c_str(), tv);
+    return true;
 }
 
 static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 032ee42..66bfc0d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -32,6 +32,8 @@
 #include <memory>
 #include <vector>
 
+#include <android-base/cmsg.h>
+
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
@@ -237,7 +239,7 @@
         CHECK(!proc->out_fds.empty());
 
         int fd = proc->out_fds.back().get();
-        if (!SendFileDescriptor(socket, fd)) {
+        if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
             D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
             goto CloseProcess;
         }
diff --git a/adb/daemon/restart_service.cpp b/adb/daemon/restart_service.cpp
index 6803d93..16d2627 100644
--- a/adb/daemon/restart_service.cpp
+++ b/adb/daemon/restart_service.cpp
@@ -20,6 +20,7 @@
 
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <log/log_properties.h>
@@ -37,6 +38,7 @@
         return;
     }
 
+    LOG(INFO) << "adbd restarting as root";
     android::base::SetProperty("service.adb.root", "1");
     WriteFdExactly(fd.get(), "restarting adbd as root\n");
 }
@@ -46,6 +48,8 @@
         WriteFdExactly(fd.get(), "adbd not running as root\n");
         return;
     }
+
+    LOG(INFO) << "adbd restarting as nonroot";
     android::base::SetProperty("service.adb.root", "0");
     WriteFdExactly(fd.get(), "restarting adbd as non root\n");
 }
@@ -56,11 +60,13 @@
         return;
     }
 
+    LOG(INFO) << "adbd restarting in TCP mode (port = " << port << ")";
     android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
     WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
 }
 
 void restart_usb_service(unique_fd fd) {
+    LOG(INFO) << "adbd restarting in USB mode";
     android::base::SetProperty("service.adb.tcp.port", "0");
     WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index d1f0345..362a987 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -244,9 +244,8 @@
 
 unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
 #if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
-    if (name.starts_with("abb:")) {
-        name.remove_prefix(strlen("abb:"));
-        return execute_binder_command(name);
+    if (name.starts_with("abb:") || name.starts_with("abb_exec:")) {
+        return execute_abb_command(name);
     }
 #endif
 
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 0794bcd..e9d9c63 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -170,6 +170,8 @@
     // Opens the file at |pts_name|.
     int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
 
+    bool ConnectProtocolEndpoints(std::string* _Nonnull error);
+
     static void ThreadHandler(void* userdata);
     void PassDataStreams();
     void WaitForExit();
@@ -383,42 +385,9 @@
     }
 
     D("subprocess parent: exec completed");
-    if (protocol_ == SubprocessProtocol::kNone) {
-        // No protocol: all streams pass through the stdinout FD and hook
-        // directly into the local socket for raw data transfer.
-        local_socket_sfd_.reset(stdinout_sfd_.release());
-    } else {
-        // 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));
-            kill(pid_, SIGKILL);
-            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";
-            kill(pid_, SIGKILL);
-            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);
-                    kill(pid_, SIGKILL);
-                    return false;
-                }
-            }
-        }
+    if (!ConnectProtocolEndpoints(error)) {
+        kill(pid_, SIGKILL);
+        return false;
     }
 
     D("subprocess parent: completed");
@@ -429,7 +398,6 @@
     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());
 
@@ -448,34 +416,9 @@
     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));
+    if (!ConnectProtocolEndpoints(error)) {
         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),
@@ -486,6 +429,45 @@
     return true;
 }
 
+bool Subprocess::ConnectProtocolEndpoints(std::string* _Nonnull error) {
+    if (protocol_ == SubprocessProtocol::kNone) {
+        // No protocol: all streams pass through the stdinout FD and hook
+        // directly into the local socket for raw data transfer.
+        local_socket_sfd_.reset(stdinout_sfd_.release());
+    } else {
+        // 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;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
 bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
     Subprocess* raw = subprocess.release();
     std::thread(ThreadHandler, raw).detach();
@@ -863,12 +845,11 @@
     return local_socket;
 }
 
-unique_fd StartCommandInProcess(std::string name, Command command) {
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol) {
     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,
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index fc66377..3abd958 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -49,7 +49,7 @@
 //
 // 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);
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
 
 // Create a pipe containing the error.
 unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 14e5071..8272722 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -416,6 +416,7 @@
                     output.strip(),
                     "already connected to {}".format(serial).encode("utf8"))
 
+    @unittest.skip("Currently failing b/123247844")
     def test_reconnect(self):
         """Ensure that a disconnected device reconnects."""
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index ae53597..0b4e084 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -52,6 +52,8 @@
 #include "fdevent.h"
 #include "sysdeps/chrono.h"
 
+using android::base::ScopedLockAssertion;
+
 static void remove_transport(atransport* transport);
 static void transport_unref(atransport* transport);
 
@@ -69,20 +71,11 @@
 const char* const kFeatureApex = "apex";
 const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
 const char* const kFeatureAbb = "abb";
+const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
+const char* const kFeatureAbbExec = "abb_exec";
 
 namespace {
 
-// A class that helps the Clang Thread Safety Analysis deal with
-// std::unique_lock. Given that std::unique_lock is movable, and the analysis
-// can not currently perform alias analysis, it is not annotated. In order to
-// assert that the mutex is held, a ScopedAssumeLocked can be created just after
-// the std::unique_lock.
-class SCOPED_CAPABILITY ScopedAssumeLocked {
-  public:
-    ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
-    ~ScopedAssumeLocked() RELEASE() {}
-};
-
 #if ADB_HOST
 // Tracks and handles atransport*s that are attempting reconnection.
 class ReconnectHandler {
@@ -180,7 +173,7 @@
         ReconnectAttempt attempt;
         {
             std::unique_lock<std::mutex> lock(reconnect_mutex_);
-            ScopedAssumeLocked assume_lock(reconnect_mutex_);
+            ScopedLockAssertion assume_lock(reconnect_mutex_);
 
             if (!reconnect_queue_.empty()) {
                 // FIXME: libstdc++ (used on Windows) implements condition_variable with
@@ -296,7 +289,7 @@
         LOG(INFO) << this->transport_name_ << ": write thread spawning";
         while (true) {
             std::unique_lock<std::mutex> lock(mutex_);
-            ScopedAssumeLocked assume_locked(mutex_);
+            ScopedLockAssertion assume_locked(mutex_);
             cv_.wait(lock, [this]() REQUIRES(mutex_) {
                 return this->stopped_ || !this->write_queue_.empty();
             });
@@ -923,7 +916,7 @@
 
 bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
     std::unique_lock<std::mutex> lock(mutex_);
-    ScopedAssumeLocked assume_locked(mutex_);
+    ScopedLockAssertion assume_locked(mutex_);
     return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
         return connection_established_ready_;
     }) && connection_established_;
@@ -1014,8 +1007,14 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-            kFeatureShell2,         kFeatureCmd,  kFeatureStat2,
-            kFeatureFixedPushMkdir, kFeatureApex, kFeatureAbb,
+            kFeatureShell2,
+            kFeatureCmd,
+            kFeatureStat2,
+            kFeatureFixedPushMkdir,
+            kFeatureApex,
+            kFeatureAbb,
+            kFeatureFixedPushSymlinkTimestamp,
+            kFeatureAbbExec,
             // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
             // to know about. Otherwise, the client can be stuck running an old
             // version of the server even after upgrading their copy of adb.
diff --git a/adb/transport.h b/adb/transport.h
index 065c81f..a0174b8 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -67,6 +67,8 @@
 extern const char* const kFeatureFixedPushMkdir;
 // adbd supports android binder bridge (abb).
 extern const char* const kFeatureAbb;
+// adbd properly updates symlink timestamps on push.
+extern const char* const kFeatureFixedPushSymlinkTimestamp;
 
 TransportId NextTransportId();
 
diff --git a/base/Android.bp b/base/Android.bp
index b0181aa..38f301a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -46,6 +46,7 @@
     defaults: ["libbase_cflags_defaults"],
     srcs: [
         "chrono_utils.cpp",
+        "cmsg.cpp",
         "file.cpp",
         "logging.cpp",
         "mapped_file.cpp",
@@ -85,6 +86,9 @@
                 "errors_windows.cpp",
                 "utf8.cpp",
             ],
+            exclude_srcs: [
+                "cmsg.cpp",
+            ],
             enabled: true,
         },
     },
@@ -121,6 +125,7 @@
     defaults: ["libbase_cflags_defaults"],
     host_supported: true,
     srcs: [
+        "cmsg_test.cpp",
         "endian_test.cpp",
         "errors_test.cpp",
         "file_test.cpp",
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 0000000..42866f8
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 <android-base/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+                                 const std::vector<int>& fds) {
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
+  size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = cmsg_len;
+
+  int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+  for (size_t i = 0; i < fds.size(); ++i) {
+    cmsg_fds[i] = fds[i];
+  }
+
+#if defined(__linux__)
+  int flags = MSG_NOSIGNAL;
+#else
+  int flags = 0;
+#endif
+
+  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, flags));
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+                                    std::vector<unique_fd>* fds) {
+  fds->clear();
+
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  int flags = MSG_TRUNC | MSG_CTRUNC;
+#if defined(__linux__)
+  flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
+#endif
+
+  ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, flags));
+
+  if (rc == -1) {
+    return -1;
+  }
+
+  int error = 0;
+  if ((msg.msg_flags & MSG_TRUNC)) {
+    LOG(ERROR) << "message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  } else if ((msg.msg_flags & MSG_CTRUNC)) {
+    LOG(ERROR) << "control message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  }
+
+  std::vector<unique_fd> received_fds;
+  struct cmsghdr* cmsg;
+  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+    if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+      LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
+                 << "]";
+      error = EBADMSG;
+      continue;
+    }
+
+    // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
+    // some asserts to ensure that CMSG_LEN behaves as we expect.
+#if defined(__linux__)
+#define CMSG_ASSERT static_assert
+#else
+// CMSG_LEN is somehow not constexpr on darwin.
+#define CMSG_ASSERT CHECK
+#endif
+    CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
+
+    if (cmsg->cmsg_len % sizeof(int) != 0) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+    } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+    }
+
+    int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+    for (size_t i = 0; i < cmsg_fdcount; ++i) {
+#if !defined(__linux__)
+      // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
+      fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
+#endif
+      received_fds.emplace_back(cmsg_fds[i]);
+    }
+  }
+
+  if (error != 0) {
+    errno = error;
+    return -1;
+  }
+
+  if (received_fds.size() > max_fds) {
+    LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+               << received_fds.size();
+    errno = EMSGSIZE;
+    return -1;
+  }
+
+  *fds = std::move(received_fds);
+  return rc;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 0000000..9ee5c82
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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 <android-base/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+  struct stat st;
+  if (fstat(fd, &st) != 0) {
+    PLOG(FATAL) << "fstat failed";
+  }
+
+  return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+  bool Seqpacket() { return GetParam(); }
+
+  void SetUp() override {
+    ASSERT_TRUE(
+        android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+    int dup1 = dup(tmp1.fd);
+    ASSERT_NE(-1, dup1);
+    int dup2 = dup(tmp2.fd);
+    ASSERT_NE(-1, dup2);
+
+    fd1.reset(dup1);
+    fd2.reset(dup2);
+
+    ino1 = GetInode(dup1);
+    ino2 = GetInode(dup2);
+  }
+
+  unique_fd send;
+  unique_fd recv;
+
+  TemporaryFile tmp1;
+  TemporaryFile tmp2;
+
+  unique_fd fd1;
+  unique_fd fd2;
+
+  ino_t ino1;
+  ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+  ASSERT_EQ('x', buf[0]);
+  ASSERT_NE(-1, received.get());
+
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+
+  ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+  if (Seqpacket()) {
+    ASSERT_EQ(-1, rc);
+    ASSERT_EQ(EMSGSIZE, errno);
+    ASSERT_EQ(-1, received1.get());
+    ASSERT_EQ(-1, received2.get());
+  } else {
+    ASSERT_EQ(1, rc);
+    ASSERT_NE(-1, received1.get());
+    ASSERT_NE(-1, received2.get());
+    ASSERT_EQ(ino1, GetInode(received1.get()));
+    ASSERT_EQ(ino2, GetInode(received2.get()));
+    ASSERT_EQ(1, read(recv.get(), buf, 2));
+  }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(EMSGSIZE, errno);
+  ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+  char buf[2];
+  ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+  ASSERT_EQ('a', buf[0]);
+
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+  if (Seqpacket()) {
+    return;
+  }
+
+  // fds are associated with the first byte of the write.
+  ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+  char buf[2];
+  ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+  ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+  std::vector<unique_fd> received1;
+  ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('c', buf[0]);
+  ASSERT_TRUE(received1.empty());
+
+  unique_fd received2;
+  rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('d', buf[0]);
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+  unique_fd sent1(dup(tmp1.fd));
+  unique_fd sent2(dup(tmp2.fd));
+
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+  char buf[2];
+  std::vector<unique_fd> received;
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 0000000..7f93ddc
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// Helpers for sending and receiving file descriptors across Unix domain sockets.
+//
+// The cmsg(3) API is very hard to get right, with multiple landmines that can
+// lead to death. Almost all of the uses of cmsg in Android make at least one of
+// the following mistakes:
+//
+//   - not aligning the cmsg buffer
+//   - leaking fds if more fds are received than expected
+//   - blindly dereferencing CMSG_DATA without checking the header
+//   - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
+//   - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
+//   - using a length specified in number of fds instead of bytes
+//
+// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
+
+// Send file descriptors across a Unix domain socket.
+//
+// Note that the write can return short if the socket type is SOCK_STREAM. When
+// this happens, file descriptors are still sent to the other end, but with
+// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
+ssize_t SendFileDescriptorVector(int sock, const void* data, size_t len,
+                                 const std::vector<int>& fds);
+
+// Receive file descriptors from a Unix domain socket.
+//
+// If more FDs (or bytes, for datagram sockets) are received than expected,
+// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
+ssize_t ReceiveFileDescriptorVector(int sock, void* data, size_t len, size_t max_fds,
+                                    std::vector<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+//   SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(int sock, const void* data, size_t len, Args&&... sent_fds) {
+  // Do not allow implicit conversion to int: people might try to do something along the lines of:
+  //   SendFileDescriptors(..., std::move(a_unique_fd))
+  // and be surprised when the unique_fd isn't closed afterwards.
+  AssertType<int>(std::forward<Args>(sent_fds)...);
+  std::vector<int> fds;
+  Append(fds, std::forward<Args>(sent_fds)...);
+  return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(int sock, void* data, size_t len, Args&&... received_fds) {
+  std::vector<unique_fd*> fds;
+  Append(fds, std::forward<Args>(received_fds)...);
+
+  std::vector<unique_fd> result;
+  ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+  if (rc == -1 || result.size() != fds.size()) {
+    int err = rc == -1 ? errno : ENOMSG;
+    for (unique_fd* fd : fds) {
+      fd->reset();
+    }
+    errno = err;
+    return -1;
+  }
+
+  for (size_t i = 0; i < fds.size(); ++i) {
+    *fds[i] = std::move(result[i]);
+  }
+  return rc;
+}
+
+#endif
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 0000000..be0683a
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+//   template <typename... Args>
+//   std::vector<int> CreateVector(Args&&... args) {
+//     std::vector<int> result;
+//     Append(result, std::forward<Args>(args)...);
+//     return result;
+//   }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+  collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+  collection.push_back(std::forward<T>(arg));
+  return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+  AssertType<T>(std::forward<Args>(args)...);
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 5c55e63..53fe6da 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <mutex>
+
 #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
 
 #define CAPABILITY(x) \
@@ -104,3 +106,39 @@
 
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+//   std::mutex mutex;
+//   std::condition_variable cv;
+//   std::vector<int> vec GUARDED_BY(mutex);
+//
+//   int pop() {
+//     std::unique_lock lock(mutex);
+//     ScopedLockAssertion lock_assertion(mutex);
+//     cv.wait(lock, []() {
+//       ScopedLockAssertion lock_assertion(mutex);
+//       return !vec.empty();
+//     });
+//
+//     int result = vec.back();
+//     vec.pop_back();
+//     return result;
+//   }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+  ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+  ~ScopedLockAssertion() RELEASE() {}
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 4a53a33..76c5ade 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -115,19 +115,14 @@
         "libbase",
         "libdebuggerd",
         "libunwindstack",
-        "libdexfile",  // libunwindstack dependency
-        "libdexfile_external",  // libunwindstack dependency
-        "libdexfile_support",  // libunwindstack dependency
+        "libdexfile_support_static",  // libunwindstack dependency
         "liblzma",
         "libcutils",
     ],
     target: {
         recovery: {
             exclude_static_libs: [
-                "libartbase",
-                "libdexfile",
-                "libdexfile_external",
-                "libdexfile_support",
+                "libdexfile_support_static",
             ],
         },
     },
@@ -174,8 +169,7 @@
     include_dirs: ["bionic/libc"],
 
     static_libs: [
-        "libdexfile_external",  // libunwindstack dependency
-        "libdexfile_support",  // libunwindstack dependency
+        "libdexfile_support_static",  // libunwindstack dependency
         "libunwindstack",
         "liblzma",
         "libbase",
@@ -185,8 +179,7 @@
     target: {
         recovery: {
             exclude_static_libs: [
-                "libdexfile_external",
-                "libdexfile_support",
+                "libdexfile_support_static",
             ],
         },
     },
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 610b96b..60eb241 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -26,6 +26,7 @@
 
 #include <chrono>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -41,6 +42,7 @@
 
 using namespace std::chrono_literals;
 
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
@@ -146,15 +148,16 @@
     PLOG(ERROR) << "failed to set pipe buffer size";
   }
 
-  if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+  pipe_write.reset();
+  if (rc != sizeof(req)) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
   }
 
   // Check to make sure we've successfully registered.
   InterceptResponse response;
-  ssize_t rc =
-      TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+  rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
     LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
                << "timeout reached?";
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index bea8b43..64df53e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -32,6 +32,7 @@
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -53,6 +54,8 @@
 #include "util.h"
 
 using namespace std::chrono_literals;
+
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 #if defined(__LP64__)
@@ -123,12 +126,14 @@
 
   ASSERT_GE(pipe_buffer_size, 1024 * 1024);
 
-  if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());
+  output_pipe_write.reset();
+  if (rc != sizeof(req)) {
     FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
   }
 
   InterceptResponse response;
-  ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+  rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
   if (rc == -1) {
     FAIL() << "failed to read response from tombstoned: " << strerror(errno);
   } else if (rc == 0) {
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index c446dbb..7d25c50 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -24,6 +24,7 @@
 #include <event2/event.h>
 #include <event2/listener.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
@@ -31,6 +32,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
@@ -96,7 +98,8 @@
   {
     unique_fd rcv_fd;
     InterceptRequest intercept_request;
-    ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+    ssize_t result =
+        ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
 
     if (result == -1) {
       PLOG(WARNING) << "failed to read from intercept socket";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index ad92067..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -31,6 +31,7 @@
 #include <event2/listener.h>
 #include <event2/thread.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -45,6 +46,7 @@
 #include "intercept_manager.h"
 
 using android::base::GetIntProperty;
+using android::base::SendFileDescriptors;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
@@ -224,7 +226,10 @@
   TombstonedCrashPacket response = {
     .packet_type = CrashPacketType::kPerformDump
   };
-  ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
+  ssize_t rc =
+      SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
+  output_fd.reset();
+
   if (rc == -1) {
     PLOG(WARNING) << "failed to send response to CrashRequest";
     goto fail;
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index bdb4c1a..2c23c98 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -21,6 +21,7 @@
 
 #include <utility>
 
+#include <android-base/cmsg.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <cutils/sockets.h>
@@ -28,6 +29,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
@@ -53,7 +55,7 @@
   }
 
   unique_fd tmp_output_fd;
-  ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+  ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
   if (rc == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                           "failed to read response to DumpRequest packet: %s", strerror(errno));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 50c5efc..a37b3b9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,73 +24,9 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include "protocol.h"
 
-using android::base::unique_fd;
-
-ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
-
-  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
-}
-
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullable out_fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov,
-    .msg_iovlen = 1,
-    .msg_control = cmsg_buf,
-    .msg_controllen = sizeof(cmsg_buf),
-    .msg_flags = 0,
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-
-  ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
-  if (result == -1) {
-    return -1;
-  }
-
-  unique_fd fd;
-  bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
-  if (received_fd) {
-    fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
-  }
-
-  if ((msg.msg_flags & MSG_TRUNC) != 0) {
-    errno = EFBIG;
-    return -1;
-  } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  if (out_fd) {
-    *out_fd = std::move(fd);
-  } else if (received_fd) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  return result;
-}
-
 std::string get_process_name(pid_t pid) {
   std::string result = "<unknown>";
   android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 8260b44..e964423 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -21,29 +21,5 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
-#include <android-base/unique_fd.h>
-
-// *** WARNING ***
-// tombstoned's sockets are SOCK_SEQPACKET sockets.
-// Short reads are treated as errors and short writes are assumed to not happen.
-
-// Sends a packet with an attached fd.
-ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
-
-// Receives a packet and optionally, its attached fd.
-// If out_fd is non-null, packets can optionally have an attached fd.
-// If out_fd is null, received packets must not have an attached fd.
-//
-// Errors:
-//   EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
-//              The first len bytes of the packet are stored in data, but the
-//              rest of the packet is dropped.
-//   ERANGE:    too many file descriptors were attached to the packet.
-//   ENOMSG:    not enough file descriptors were attached to the packet.
-//
-//   plus any errors returned by the underlying recvmsg.
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
-                android::base::unique_fd* _Nullable out_fd);
-
 std::string get_process_name(pid_t pid);
 std::string get_thread_name(pid_t tid);
diff --git a/adf/Android.bp b/deprecated-adf/Android.bp
similarity index 100%
rename from adf/Android.bp
rename to deprecated-adf/Android.bp
diff --git a/adf/OWNERS b/deprecated-adf/OWNERS
similarity index 100%
rename from adf/OWNERS
rename to deprecated-adf/OWNERS
diff --git a/adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
similarity index 100%
rename from adf/libadf/Android.bp
rename to deprecated-adf/libadf/Android.bp
diff --git a/adf/libadf/adf.cpp b/deprecated-adf/libadf/adf.cpp
similarity index 100%
rename from adf/libadf/adf.cpp
rename to deprecated-adf/libadf/adf.cpp
diff --git a/adf/libadf/include/adf/adf.h b/deprecated-adf/libadf/include/adf/adf.h
similarity index 100%
rename from adf/libadf/include/adf/adf.h
rename to deprecated-adf/libadf/include/adf/adf.h
diff --git a/adf/libadf/include/video/adf.h b/deprecated-adf/libadf/include/video/adf.h
similarity index 100%
rename from adf/libadf/include/video/adf.h
rename to deprecated-adf/libadf/include/video/adf.h
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
similarity index 100%
rename from adf/libadf/original-kernel-headers/video/adf.h
rename to deprecated-adf/libadf/original-kernel-headers/video/adf.h
diff --git a/adf/libadf/tests/Android.bp b/deprecated-adf/libadf/tests/Android.bp
similarity index 100%
rename from adf/libadf/tests/Android.bp
rename to deprecated-adf/libadf/tests/Android.bp
diff --git a/adf/libadf/tests/adf_test.cpp b/deprecated-adf/libadf/tests/adf_test.cpp
similarity index 100%
rename from adf/libadf/tests/adf_test.cpp
rename to deprecated-adf/libadf/tests/adf_test.cpp
diff --git a/adf/libadfhwc/Android.bp b/deprecated-adf/libadfhwc/Android.bp
similarity index 100%
rename from adf/libadfhwc/Android.bp
rename to deprecated-adf/libadfhwc/Android.bp
diff --git a/adf/libadfhwc/adfhwc.cpp b/deprecated-adf/libadfhwc/adfhwc.cpp
similarity index 100%
rename from adf/libadfhwc/adfhwc.cpp
rename to deprecated-adf/libadfhwc/adfhwc.cpp
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
similarity index 100%
rename from adf/libadfhwc/include/adfhwc/adfhwc.h
rename to deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index b69e773..0c904c4 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1098,9 +1098,7 @@
             }
         } else if ((current_entry.fs_mgr_flags.verify)) {
             int rc = fs_mgr_setup_verity(&current_entry, true);
-            if (__android_log_is_debuggable() &&
-                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
-                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
@@ -1331,9 +1329,7 @@
             }
         } else if (fstab_entry.fs_mgr_flags.verify) {
             int rc = fs_mgr_setup_verity(&fstab_entry, true);
-            if (__android_log_is_debuggable() &&
-                    (rc == FS_MGR_SETUP_VERITY_DISABLED ||
-                     rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
                 LINFO << "Verity disabled";
             } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
                 LERROR << "Could not set up verified partition, skipping!";
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4d44fcf..82d9144 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -588,18 +588,7 @@
     return boot_devices;
 }
 
-void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
-    auto iter = std::remove_if(fstab->begin(), fstab->end(),
-                               [&](const auto& entry) { return entry.mount_point == mount_point; });
-    fstab->erase(iter, fstab->end());
-}
-
-void TransformFstabForGsi(Fstab* fstab) {
-    EraseFstabEntry(fstab, "/system");
-    EraseFstabEntry(fstab, "/data");
-
-    fstab->emplace_back(BuildGsiSystemFstabEntry());
-
+FstabEntry BuildGsiUserdataFstabEntry() {
     constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
 
     FstabEntry userdata = {
@@ -615,7 +604,41 @@
     userdata.fs_mgr_flags.quota = true;
     userdata.fs_mgr_flags.late_mount = true;
     userdata.fs_mgr_flags.formattable = true;
-    fstab->emplace_back(userdata);
+    return userdata;
+}
+
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+    auto iter = std::remove_if(fstab->begin(), fstab->end(),
+                               [&](const auto& entry) { return entry.mount_point == mount_point; });
+    if (iter != fstab->end()) {
+        fstab->erase(iter, fstab->end());
+        return true;
+    }
+    return false;
+}
+
+void TransformFstabForGsi(Fstab* fstab) {
+    // Inherit fstab properties for userdata.
+    FstabEntry userdata;
+    if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
+        userdata = *entry;
+        userdata.blk_device = "userdata_gsi";
+        userdata.fs_mgr_flags.logical = true;
+        userdata.fs_mgr_flags.formattable = true;
+        if (!userdata.key_dir.empty()) {
+            userdata.key_dir += "/gsi";
+        }
+    } else {
+        userdata = BuildGsiUserdataFstabEntry();
+    }
+
+    if (EraseFstabEntry(fstab, "/system")) {
+        fstab->emplace_back(BuildGsiSystemFstabEntry());
+    }
+
+    if (EraseFstabEntry(fstab, "/data")) {
+        fstab->emplace_back(userdata);
+    }
 }
 
 }  // namespace
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 87729cd..3d5062c 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -78,6 +78,10 @@
 
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
+    return {};
+}
+
 bool fs_mgr_overlayfs_mount_all(Fstab*) {
     return false;
 }
@@ -153,7 +157,12 @@
         fs_mgr_update_logical_partition(entry);
     }
     auto save_errno = errno;
+    errno = 0;
     auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
+    // special case for first stage init for system as root (taimen)
+    if (!has_shared_blocks && (errno == ENOENT) && (entry->blk_device == "/dev/root")) {
+        has_shared_blocks = true;
+    }
     errno = save_errno;
     return has_shared_blocks;
 }
@@ -238,8 +247,7 @@
     return ret;
 }
 
-const char* fs_mgr_mount_point(const char* mount_point) {
-    if (!mount_point) return mount_point;
+const std::string fs_mgr_mount_point(const std::string& mount_point) {
     if ("/"s != mount_point) return mount_point;
     return "/system";
 }
@@ -526,40 +534,6 @@
     }
 }
 
-std::vector<std::string> fs_mgr_candidate_list(Fstab* fstab, const char* mount_point = nullptr) {
-    std::vector<std::string> mounts;
-    for (auto& entry : *fstab) {
-        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
-            !fs_mgr_wants_overlayfs(&entry)) {
-            continue;
-        }
-        std::string new_mount_point(fs_mgr_mount_point(entry.mount_point.c_str()));
-        if (mount_point && (new_mount_point != mount_point)) continue;
-
-        auto saved_errno = errno;
-        auto verity_enabled = fs_mgr_is_verity_enabled(entry);
-        if (errno == ENOENT || errno == ENXIO) errno = saved_errno;
-        if (verity_enabled) continue;
-
-        auto duplicate_or_more_specific = false;
-        for (auto it = mounts.begin(); it != mounts.end();) {
-            if ((*it == new_mount_point) ||
-                (android::base::StartsWith(new_mount_point, *it + "/"))) {
-                duplicate_or_more_specific = true;
-                break;
-            }
-            if (android::base::StartsWith(*it, new_mount_point + "/")) {
-                it = mounts.erase(it);
-            } else {
-                ++it;
-            }
-        }
-        if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
-    }
-
-    return mounts;
-}
-
 // Mount kScratchMountPoint
 bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
                                     bool readonly = false) {
@@ -802,12 +776,42 @@
 
 }  // namespace
 
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+    Fstab candidates;
+    for (const auto& entry : fstab) {
+        FstabEntry new_entry = entry;
+        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
+            !fs_mgr_wants_overlayfs(&new_entry)) {
+            continue;
+        }
+        auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
+        auto duplicate_or_more_specific = false;
+        for (auto it = candidates.begin(); it != candidates.end();) {
+            auto it_mount_point = fs_mgr_mount_point(it->mount_point);
+            if ((it_mount_point == new_mount_point) ||
+                (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
+                duplicate_or_more_specific = true;
+                break;
+            }
+            if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
+                it = candidates.erase(it);
+            } else {
+                ++it;
+            }
+        }
+        if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+    }
+    return candidates;
+}
+
 bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
     auto ret = false;
     if (fs_mgr_overlayfs_invalid()) return ret;
 
     auto scratch_can_be_mounted = true;
-    for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        auto mount_point = fs_mgr_mount_point(entry.mount_point);
         if (fs_mgr_overlayfs_already_mounted(mount_point)) {
             ret = true;
             continue;
@@ -840,8 +844,9 @@
         return {};
     }
 
-    for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
-        if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) continue;
         auto device = fs_mgr_overlayfs_scratch_device();
         if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
         return {device};
@@ -867,8 +872,24 @@
         return false;
     }
     errno = save_errno;
-    auto mounts = fs_mgr_candidate_list(&fstab, fs_mgr_mount_point(mount_point));
-    if (mounts.empty()) return ret;
+    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+    for (auto it = candidates.begin(); it != candidates.end();) {
+        if (mount_point &&
+            (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
+            it = candidates.erase(it);
+            continue;
+        }
+        save_errno = errno;
+        auto verity_enabled = fs_mgr_is_verity_enabled(*it);
+        if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+        if (verity_enabled) {
+            it = candidates.erase(it);
+            continue;
+        }
+        ++it;
+    }
+
+    if (candidates.empty()) return ret;
 
     std::string dir;
     for (const auto& overlay_mount_point : kOverlayMountPoints) {
@@ -891,8 +912,8 @@
 
     std::string overlay;
     ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
-    for (const auto& fsrec_mount_point : mounts) {
-        ret |= fs_mgr_overlayfs_setup_one(overlay, fsrec_mount_point, change);
+    for (const auto& entry : candidates) {
+        ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
     }
     return ret;
 }
@@ -901,7 +922,6 @@
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
     if (change) *change = false;
-    mount_point = fs_mgr_mount_point(mount_point);
     auto ret = true;
     // If scratch exists, but is not mounted, lets gain access to clean
     // specific override entries.
@@ -919,7 +939,8 @@
                                                        fs_mgr_overlayfs_scratch_mount_type());
     }
     for (const auto& overlay_mount_point : kOverlayMountPoints) {
-        ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
+        ret &= fs_mgr_overlayfs_teardown_one(
+                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
     }
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
         // After obligatory teardown to make sure everything is clean, but if
@@ -946,8 +967,9 @@
         return false;
     }
     if (fs_mgr_overlayfs_invalid()) return false;
-    for (const auto& mount_point : fs_mgr_candidate_list(&fstab)) {
-        if (fs_mgr_overlayfs_already_mounted(mount_point)) return true;
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
     }
     return false;
 }
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 64682cc..6aaf1f3 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,6 +21,8 @@
 #include <string>
 #include <vector>
 
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+
 bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
 std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index b9b75f8..5b8a9c2 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -225,7 +225,7 @@
     // don't come back unwritten. Return from this function with the kernel file offset set to 0.
     // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
     // aren't moved around.
-    if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+    if (fallocate64(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
         PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
         return false;
     }
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 3e93265..a3c76ab 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -24,6 +24,8 @@
         "avb_ops.cpp",
         "avb_util.cpp",
         "fs_avb.cpp",
+        "fs_avb_util.cpp",
+        "types.cpp",
         "util.cpp",
     ],
     static_libs: [
@@ -98,6 +100,7 @@
     srcs: [
         "tests/basic_test.cpp",
         "tests/fs_avb_test.cpp",
+        "tests/fs_avb_util_test.cpp",
     ],
 }
 
@@ -115,3 +118,26 @@
         "tests/util_test.cpp",
     ],
 }
+
+cc_test {
+    name: "libfs_avb_device_test",
+    test_suites: ["device-tests"],
+    static_libs: [
+        "libavb",
+        "libdm",
+        "libfs_avb",
+        "libfstab",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+    ],
+    srcs: [
+        "tests/fs_avb_device_test.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index c0f12aa..a849d94 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -27,7 +27,7 @@
 #include <string>
 #include <vector>
 
-#include <fs_avb/fs_avb.h>
+#include <fs_avb/types.h>
 #include <libavb/libavb.h>
 
 namespace android {
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 08f87b4..80fa5c4 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -34,56 +34,24 @@
 namespace android {
 namespace fs_mgr {
 
-// Helper functions to print enum class VBMetaVerifyResult.
-const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
-    // clang-format off
-    static const char* const name[] = {
-        "ResultSuccess",
-        "ResultError",
-        "ResultErrorVerification",
-        "ResultUnknown",
-    };
-    // clang-format on
-
-    uint32_t index = static_cast<uint32_t>(result);
-    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
-    if (index >= unknown_index) {
-        index = unknown_index;
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images) {
+    size_t value_size;
+    for (const auto& vbmeta : vbmeta_images) {
+        const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
+                                                key.size(), &value_size);
+        if (value != nullptr) {
+            return {value, value_size};
+        }
     }
-
-    return name[index];
-}
-
-std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
-    os << VBMetaVerifyResultToString(result);
-    return os;
-}
-
-// class VBMetaData
-// ----------------
-std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
-    auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();
-
-    if (!vbmeta_header) return nullptr;
-
-    /* Byteswap the header. */
-    avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
-                                               vbmeta_header.get());
-    if (update_vbmeta_size) {
-        vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
-                       vbmeta_header->authentication_data_block_size +
-                       vbmeta_header->auxiliary_data_block_size;
-    }
-
-    return vbmeta_header;
+    return "";
 }
 
 // Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
 // See the following link for more details:
 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt,
-                          const std::string& root_digest, const std::string& blk_device,
-                          android::dm::DmTable* table) {
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+                          const std::string& blk_device, android::dm::DmTable* table) {
     // Loads androidboot.veritymode from kernel cmdline.
     std::string verity_mode;
     if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
@@ -104,12 +72,12 @@
     std::ostringstream hash_algorithm;
     hash_algorithm << hashtree_desc.hash_algorithm;
 
-    android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
-                                       hashtree_desc.dm_verity_version, blk_device, blk_device,
-                                       hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
-                                       hashtree_desc.image_size / hashtree_desc.data_block_size,
-                                       hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
-                                       hash_algorithm.str(), root_digest, salt);
+    android::dm::DmTargetVerity target(
+            0, hashtree_desc.image_size / 512, hashtree_desc.dm_verity_version, blk_device,
+            blk_device, hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+            hashtree_desc.image_size / hashtree_desc.data_block_size,
+            hashtree_desc.tree_offset / hashtree_desc.hash_block_size, hash_algorithm.str(),
+            hashtree_desc.root_digest, hashtree_desc.salt);
     if (hashtree_desc.fec_size > 0) {
         target.UseFec(blk_device, hashtree_desc.fec_num_roots,
                       hashtree_desc.fec_offset / hashtree_desc.data_block_size,
@@ -126,12 +94,10 @@
     return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
 }
 
-bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& hashtree_desc,
-                           const std::string& salt, const std::string& root_digest,
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
                            bool wait_for_verity_dev) {
     android::dm::DmTable table;
-    if (!ConstructVerityTable(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
-        !table.valid()) {
+    if (!ConstructVerityTable(hashtree_desc, fstab_entry->blk_device, &table) || !table.valid()) {
         LERROR << "Failed to construct verity table.";
         return false;
     }
@@ -164,12 +130,11 @@
     return true;
 }
 
-std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor(
-    const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images,
-    std::string* out_salt, std::string* out_digest) {
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
     bool found = false;
     const uint8_t* desc_partition_name;
-    auto hashtree_desc = std::make_unique<AvbHashtreeDescriptor>();
+    auto hashtree_desc = std::make_unique<FsAvbHashtreeDescriptor>();
 
     for (const auto& vbmeta : vbmeta_images) {
         size_t num_descriptors;
@@ -209,15 +174,17 @@
     }
 
     if (!found) {
-        LERROR << "Partition descriptor not found: " << partition_name.c_str();
+        LERROR << "Hashtree descriptor not found: " << partition_name;
         return nullptr;
     }
 
+    hashtree_desc->partition_name = partition_name;
+
     const uint8_t* desc_salt = desc_partition_name + hashtree_desc->partition_name_len;
-    *out_salt = BytesToHex(desc_salt, hashtree_desc->salt_len);
+    hashtree_desc->salt = BytesToHex(desc_salt, hashtree_desc->salt_len);
 
     const uint8_t* desc_digest = desc_salt + hashtree_desc->salt_len;
-    *out_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);
+    hashtree_desc->root_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);
 
     return hashtree_desc;
 }
@@ -235,18 +202,15 @@
         return false;
     }
 
-    std::string salt;
-    std::string root_digest;
-    std::unique_ptr<AvbHashtreeDescriptor> hashtree_descriptor =
-        GetHashtreeDescriptor(partition_name, vbmeta_images, &salt, &root_digest);
+    std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =
+            GetHashtreeDescriptor(partition_name, vbmeta_images);
     if (!hashtree_descriptor) {
         return false;
     }
 
     // Converts HASHTREE descriptor to verity table to load into kernel.
     // When success, the new device path will be returned, e.g., /dev/block/dm-2.
-    return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, salt, root_digest,
-                                 wait_for_verity_dev);
+    return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, wait_for_verity_dev);
 }
 
 // Converts a AVB partition_name (without A/B suffix) to a device partition name.
@@ -420,6 +384,10 @@
     uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;
     bool is_vbmeta_partition = StartsWith(partition_name, "vbmeta");
 
+    if (out_verify_result) {
+        *out_verify_result = VBMetaVerifyResult::kError;
+    }
+
     if (!is_vbmeta_partition) {
         std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
         if (!footer) {
@@ -445,7 +413,10 @@
 
     auto verify_result =
             VerifyVBMetaSignature(*vbmeta, expected_public_key_blob, out_public_key_data);
-    if (out_verify_result != nullptr) *out_verify_result = verify_result;
+
+    if (out_verify_result != nullptr) {
+        *out_verify_result = verify_result;
+    }
 
     if (verify_result == VBMetaVerifyResult::kSuccess ||
         verify_result == VBMetaVerifyResult::kErrorVerification) {
@@ -508,6 +479,10 @@
         const std::string& expected_public_key_blob, bool allow_verification_error,
         bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
         bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result) {
+    if (out_verify_result) {
+        *out_verify_result = VBMetaVerifyResult::kError;
+    }
+
     // Ensures the device path (might be a symlink created by init) is ready to access.
     if (!WaitForFile(image_path, 1s)) {
         PERROR << "No such path: " << image_path;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 4b54e27..5f413e3 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -24,19 +24,11 @@
 #include <libavb/libavb.h>
 #include <libdm/dm.h>
 
-#include "fs_avb/fs_avb.h"
+#include "fs_avb/types.h"
 
 namespace android {
 namespace fs_mgr {
 
-enum class VBMetaVerifyResult {
-    kSuccess = 0,
-    kError = 1,
-    kErrorVerification = 2,
-};
-
-std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
-
 struct ChainInfo {
     std::string partition_name;
     std::string public_key_blob;
@@ -45,17 +37,17 @@
         : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
 };
 
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images);
+
 // AvbHashtreeDescriptor to dm-verity table setup.
-std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor(
-    const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images,
-    std::string* out_salt, std::string* out_digest);
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
 
-bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt,
-                          const std::string& root_digest, const std::string& blk_device,
-                          android::dm::DmTable* table);
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+                          const std::string& blk_device, android::dm::DmTable* table);
 
-bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& hashtree_desc,
-                           const std::string& salt, const std::string& root_digest,
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
                            bool wait_for_verity_dev);
 
 // Searches a Avb hashtree descriptor in vbmeta_images for fstab_entry, to enable dm-verity.
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 1af3b33..02902f0 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -76,33 +76,6 @@
     return std::make_pair(digest, total_size);
 }
 
-// Helper functions to dump enum class AvbHandleStatus.
-const char* AvbHandleStatusToString(AvbHandleStatus status) {
-    // clang-format off
-    static const char* const name[] = {
-        "Success",
-        "Uninitialized",
-        "HashtreeDisabled",
-        "VerificationDisabled",
-        "VerificationError",
-        "Unknown",
-    };
-    // clang-format on
-
-    uint32_t index = static_cast<uint32_t>(status);
-    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
-    if (index >= unknown_index) {
-        index = unknown_index;
-    }
-
-    return name[index];
-}
-
-std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
-    os << AvbHandleStatusToString(status);
-    return os;
-}
-
 // class AvbVerifier
 // -----------------
 // Reads the following values from kernel cmdline and provides the
@@ -290,6 +263,69 @@
     return avb_handle;
 }
 
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
+    if (fstab_entry.avb_key.empty()) {
+        LERROR << "avb_key=/path/to/key is missing for " << fstab_entry.mount_point;
+        return nullptr;
+    }
+
+    // Binds allow_verification_error and rollback_protection to device unlock state.
+    bool allow_verification_error = IsDeviceUnlocked();
+    bool rollback_protection = !allow_verification_error;
+
+    std::string expected_key_blob;
+    if (!ReadFileToString(fstab_entry.avb_key, &expected_key_blob)) {
+        if (!allow_verification_error) {
+            LERROR << "Failed to load avb_key: " << fstab_entry.avb_key
+                   << " for mount point: " << fstab_entry.mount_point;
+            return nullptr;
+        }
+        LWARNING << "Allowing no expected key blob when verification error is permitted";
+        expected_key_blob.clear();
+    }
+
+    bool verification_disabled = false;
+    VBMetaVerifyResult verify_result = VBMetaVerifyResult::kError;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+            fstab_entry.blk_device, "" /* partition_name, no need for a standalone path */,
+            expected_key_blob, allow_verification_error, rollback_protection,
+            false /* not is_chained_vbmeta */, nullptr /* out_public_key_data */,
+            &verification_disabled, &verify_result);
+
+    if (!vbmeta) {
+        LERROR << "Failed to load vbmeta: " << fstab_entry.blk_device;
+        return nullptr;
+    }
+
+    AvbUniquePtr avb_handle(new AvbHandle());
+    if (!avb_handle) {
+        LERROR << "Failed to allocate AvbHandle";
+        return nullptr;
+    }
+    avb_handle->vbmeta_images_.emplace_back(std::move(*vbmeta));
+
+    switch (verify_result) {
+        case VBMetaVerifyResult::kSuccess:
+            avb_handle->status_ = AvbHandleStatus::kSuccess;
+            break;
+        case VBMetaVerifyResult::kErrorVerification:
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
+            break;
+        default:
+            LERROR << "LoadAndVerifyVbmetaByPath failed, result: " << verify_result;
+            return nullptr;
+    }
+
+    if (verification_disabled) {
+        LINFO << "AVB verification disabled on: " << fstab_entry.mount_point;
+        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+    }
+
+    LINFO << "Returning avb_handle for '" << fstab_entry.mount_point
+          << "' with status: " << avb_handle->status_;
+    return avb_handle;
+}
+
 AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
     // Loads inline vbmeta images, starting from /vbmeta.
     return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
@@ -385,52 +421,12 @@
 
 AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
                                                         bool wait_for_verity_dev) {
-    if (fstab_entry->avb_key.empty()) {
-        LERROR << "avb_key=/path/to/key is missing for " << fstab_entry->mount_point;
+    auto avb_handle = LoadAndVerifyVbmeta(*fstab_entry);
+    if (!avb_handle) {
         return AvbHashtreeResult::kFail;
     }
 
-    // Binds allow_verification_error and rollback_protection to device unlock state.
-    bool allow_verification_error = IsDeviceUnlocked();
-    bool rollback_protection = !allow_verification_error;
-
-    std::string expected_key_blob;
-    if (!ReadFileToString(fstab_entry->avb_key, &expected_key_blob)) {
-        if (!allow_verification_error) {
-            LERROR << "Failed to load avb_key: " << fstab_entry->avb_key
-                   << " for mount point: " << fstab_entry->mount_point;
-            return AvbHashtreeResult::kFail;
-        }
-        LWARNING << "Allowing no expected key blob when verification error is permitted";
-        expected_key_blob.clear();
-    }
-
-    bool verification_disabled = false;
-    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
-            fstab_entry->blk_device, "" /* partition_name, no need for a standalone path */,
-            expected_key_blob, allow_verification_error, rollback_protection,
-            false /* not is_chained_vbmeta */, nullptr /* out_public_key_data */,
-            &verification_disabled, nullptr /* out_verify_result */);
-
-    if (!vbmeta) {
-        LERROR << "Failed to load vbmeta: " << fstab_entry->blk_device;
-        return AvbHashtreeResult::kFail;
-    }
-
-    if (verification_disabled) {
-        LINFO << "AVB verification disabled on: " << fstab_entry->mount_point;
-        return AvbHashtreeResult::kDisabled;
-    }
-
-    // Puts the vbmeta into a vector, for LoadAvbHashtreeToEnableVerity() to use.
-    std::vector<VBMetaData> vbmeta_images;
-    vbmeta_images.emplace_back(std::move(*vbmeta));
-    if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images,
-                                       fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
-        return AvbHashtreeResult::kFail;
-    }
-
-    return AvbHashtreeResult::kSuccess;
+    return avb_handle->SetUpAvbHashtree(fstab_entry, wait_for_verity_dev);
 }
 
 AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
@@ -452,5 +448,19 @@
     return AvbHashtreeResult::kSuccess;
 }
 
+std::string AvbHandle::GetSecurityPatchLevel(const FstabEntry& fstab_entry) const {
+    if (vbmeta_images_.size() < 1) {
+        return "";
+    }
+    std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+                                                            fs_mgr_get_other_slot_suffix());
+    auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
+    return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
+}
+
+bool AvbHandle::IsDeviceUnlocked() {
+    return android::fs_mgr::IsDeviceUnlocked();
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
new file mode 100644
index 0000000..f82f83d
--- /dev/null
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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/fs_avb_util.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "avb_util.h"
+#include "util.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+                                                const std::string& expected_public_key_blob,
+                                                std::string* out_public_key_data,
+                                                std::string* out_avb_partition_name,
+                                                VBMetaVerifyResult* out_verify_result) {
+    // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+    // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+    std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+                                                            fs_mgr_get_other_slot_suffix());
+    if (out_avb_partition_name) {
+        *out_avb_partition_name = avb_partition_name;
+    }
+
+    // Updates fstab_entry->blk_device from <partition> to /dev/block/dm-<N> if
+    // it's a logical partition.
+    std::string device_path = fstab_entry.blk_device;
+    if (fstab_entry.fs_mgr_flags.logical &&
+        !android::base::StartsWith(fstab_entry.blk_device, "/")) {
+        dm::DeviceMapper& dm = dm::DeviceMapper::Instance();
+        if (!dm.GetDmDevicePathByName(fstab_entry.blk_device, &device_path)) {
+            LERROR << "Failed to resolve logical device path for: " << fstab_entry.blk_device;
+            return nullptr;
+        }
+    }
+
+    return LoadAndVerifyVbmetaByPath(device_path, avb_partition_name, expected_public_key_blob,
+                                     true /* allow_verification_error */,
+                                     false /* rollback_protection */, false /* is_chained_vbmeta */,
+                                     out_public_key_data, nullptr /* out_verification_disabled */,
+                                     out_verify_result);
+}
+
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& avb_partition_name, VBMetaData&& vbmeta) {
+    if (!vbmeta.size()) return nullptr;
+
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+    return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
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 d4e3a6e..7127fa6 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -21,32 +21,13 @@
 #include <string>
 #include <vector>
 
+#include <fs_avb/types.h>
 #include <fstab/fstab.h>
 #include <libavb/libavb.h>
 
 namespace android {
 namespace fs_mgr {
 
-enum class AvbHashtreeResult {
-    kSuccess = 0,
-    kFail,
-    kDisabled,
-};
-
-enum class HashAlgorithm {
-    kInvalid = 0,
-    kSHA256 = 1,
-    kSHA512 = 2,
-};
-
-enum class AvbHandleStatus {
-    kSuccess = 0,
-    kUninitialized = 1,
-    kHashtreeDisabled = 2,
-    kVerificationDisabled = 3,
-    kVerificationError = 4,
-};
-
 struct VBMetaInfo {
     std::string digest;
     HashAlgorithm hash_algorithm;
@@ -58,51 +39,6 @@
         : digest(std::move(digest_value)), hash_algorithm(algorithm), total_size(size) {}
 };
 
-class VBMetaData {
-  public:
-    // Constructors
-    VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
-
-    VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
-        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
-          vbmeta_size_(size),
-          partition_name_(partition_name) {
-        // 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, const std::string& partition_name)
-        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
-          vbmeta_size_(size),
-          partition_name_(partition_name) {}
-
-    // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
-    // true to update vbmeta_size_ to the actual size with valid content.
-    std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
-
-    // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.
-    // e.g.,
-    // - /dev/block/by-name/system_a
-    // - /path/to/system_other.img.
-    void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }
-
-    // Get methods for each data member.
-    const std::string& partition() const { return partition_name_; }
-    const std::string& vbmeta_path() const { return vbmeta_path_; }
-    uint8_t* data() const { return vbmeta_ptr_.get(); }
-    const size_t& size() const { return vbmeta_size_; }
-
-    // Maximum size of a vbmeta data - 64 KiB.
-    static const size_t kMaxVBMetaSize = 64 * 1024;
-
-  private:
-    std::unique_ptr<uint8_t[]> vbmeta_ptr_;
-    size_t vbmeta_size_;
-    std::string partition_name_;
-    std::string vbmeta_path_;
-};
-
 class FsManagerAvbOps;
 
 class AvbHandle;
@@ -149,6 +85,8 @@
     // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
     static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
     static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
+    static AvbUniquePtr LoadAndVerifyVbmeta(
+            const FstabEntry& fstab_entry);     // loads offline vbmeta.
     static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.
             const std::string& partition_name, const std::string& ab_suffix,
             const std::string& ab_other_suffix, const std::string& expected_public_key,
@@ -172,6 +110,10 @@
     static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
                                                         bool wait_for_verity_dev = true);
 
+    static bool IsDeviceUnlocked();
+
+    std::string GetSecurityPatchLevel(const FstabEntry& fstab_entry) const;
+
     const std::string& avb_version() const { return avb_version_; }
     const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
     AvbHandleStatus status() const { return status_; }
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
new file mode 100644
index 0000000..ec8badb
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+                                                const std::string& expected_public_key_blob,
+                                                std::string* out_public_key_data,
+                                                std::string* out_avb_partition_name,
+                                                VBMetaVerifyResult* out_verify_result);
+
+// Gets the hashtree descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+        const std::string& avb_partition_name, VBMetaData&& vbmeta);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
new file mode 100644
index 0000000..bd638e6
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -0,0 +1,110 @@
+/*
+ * 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 <cstring>
+#include <memory>
+#include <ostream>
+
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+enum class VBMetaVerifyResult {
+    kSuccess = 0,
+    kError = 1,
+    kErrorVerification = 2,
+};
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
+
+enum class AvbHashtreeResult {
+    kSuccess = 0,
+    kFail,
+    kDisabled,
+};
+
+enum class HashAlgorithm {
+    kInvalid = 0,
+    kSHA256 = 1,
+    kSHA512 = 2,
+};
+
+enum class AvbHandleStatus {
+    kSuccess = 0,
+    kUninitialized = 1,
+    kHashtreeDisabled = 2,
+    kVerificationDisabled = 3,
+    kVerificationError = 4,
+};
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
+
+struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
+    std::string partition_name;
+    std::string salt;
+    std::string root_digest;
+};
+
+class VBMetaData {
+  public:
+    // Constructors
+    VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
+
+    VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+          vbmeta_size_(size),
+          partition_name_(partition_name) {
+        // The ownership of data is NOT transferred, i.e., the caller still
+        // needs to release the memory as we make a copy here.
+        std::memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
+    }
+
+    explicit VBMetaData(size_t size, const std::string& partition_name)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+          vbmeta_size_(size),
+          partition_name_(partition_name) {}
+
+    // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
+    // true to update vbmeta_size_ to the actual size with valid content.
+    std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
+
+    // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.
+    // e.g.,
+    // - /dev/block/by-name/system_a
+    // - /path/to/system_other.img.
+    void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }
+
+    // Get methods for each data member.
+    const std::string& partition() const { return partition_name_; }
+    const std::string& vbmeta_path() const { return vbmeta_path_; }
+    uint8_t* data() const { return vbmeta_ptr_.get(); }
+    const size_t& size() const { return vbmeta_size_; }
+
+    // Maximum size of a vbmeta data - 64 KiB.
+    static const size_t kMaxVBMetaSize = 64 * 1024;
+
+  private:
+    std::unique_ptr<uint8_t[]> vbmeta_ptr_;
+    size_t vbmeta_size_;
+    std::string partition_name_;
+    std::string vbmeta_path_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/run_tests.sh b/fs_mgr/libfs_avb/run_tests.sh
new file mode 100755
index 0000000..5d2ce3d
--- /dev/null
+++ b/fs_mgr/libfs_avb/run_tests.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Run host tests
+atest libfs_avb_test                 # Tests public libfs_avb APIs.
+atest libfs_avb_internal_test        # Tests libfs_avb private APIs.
+
+# Run device tests
+atest libfs_avb_device_test          # Test public libfs_avb APIs on a device.
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 835410f..e4213b7 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -30,6 +30,7 @@
 using android::fs_mgr::DeriveAvbPartitionName;
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::GetAvbFooter;
+using android::fs_mgr::GetAvbPropertyDescriptor;
 using android::fs_mgr::GetChainPartitionInfo;
 using android::fs_mgr::GetTotalSize;
 using android::fs_mgr::LoadAndVerifyVbmetaByPartition;
@@ -268,6 +269,67 @@
     EXPECT_EQ(nullptr, footer);
 }
 
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_Basic) {
+    // Makes a vbmeta.img with some properties.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+                        {}, /* include_descriptor_image_paths */
+                        {}, /* chain_partitions */
+                        "--prop foo:android "
+                        "--prop bar:treble "
+                        "--internal_release_string \"unit test\" ");
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+
+    EXPECT_EQ("android", GetAvbPropertyDescriptor("foo", vbmeta_images));
+    EXPECT_EQ("treble", GetAvbPropertyDescriptor("bar", vbmeta_images));
+    EXPECT_EQ("", GetAvbPropertyDescriptor("non-existent", vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_SecurityPatchLevel) {
+    // 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,
+                 data_dir_.Append("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,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--prop com.android.build.system.security_patch:2019-04-05 "
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta.img including the 'system' chained descriptor.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+                        {boot_path},                         /* include_descriptor_image_paths */
+                        {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(vbmeta));
+    vbmeta_images.emplace_back(std::move(system_vbmeta));
+
+    EXPECT_EQ("2019-04-05",
+              GetAvbPropertyDescriptor("com.android.build.system.security_patch", vbmeta_images));
+}
+
 TEST_F(AvbUtilTest, GetVBMetaHeader) {
     // Generates a raw boot.img
     const size_t image_size = 5 * 1024 * 1024;
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
new file mode 100644
index 0000000..4631330
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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 <android-base/properties.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_avb/fs_avb_util.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+
+namespace fs_avb_device_test {
+
+// system vbmeta might not be at the end of /system when dynamic partition is
+// enabled. Therefore, disable it by default.
+TEST(FsAvbUtilTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) {
+    Fstab fstab;
+    EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+    FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+    EXPECT_NE(nullptr, system_entry);
+
+    std::string out_public_key_data;
+    std::string out_avb_partition_name;
+    VBMetaVerifyResult out_verify_result;
+    std::unique_ptr<VBMetaData> vbmeta =
+            LoadAndVerifyVbmeta(*system_entry, "" /* expected_public_key_blob */,
+                                &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+    EXPECT_EQ("system", out_avb_partition_name);
+    EXPECT_NE("", out_public_key_data);
+}
+
+TEST(FsAvbUtilTest, GetHashtreeDescriptor_SystemOther) {
+    // Non-A/B device doesn't have system_other partition.
+    if (fs_mgr_get_slot_suffix() == "") return;
+
+    // Skip running this test if system_other is a logical partition.
+    // Note that system_other is still a physical partition on "retrofit" devices.
+    if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+        !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+        return;
+    }
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+    // It should have two lines in the fstab, the first for logical system_other,
+    // the other for physical system_other.
+    EXPECT_EQ(2UL, fstab.size());
+
+    // Use the 2nd fstab entry, which is for physical system_other partition.
+    FstabEntry* system_other = &fstab[1];
+    EXPECT_NE(nullptr, system_other);
+
+    std::string out_public_key_data;
+    std::string out_avb_partition_name;
+    VBMetaVerifyResult out_verify_result;
+    std::unique_ptr<VBMetaData> system_other_vbmeta =
+            LoadAndVerifyVbmeta(*system_other, "" /* expected_public_key_blob */,
+                                &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+    EXPECT_NE(nullptr, system_other_vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+    EXPECT_EQ("system_other", out_avb_partition_name);
+    EXPECT_NE("", out_public_key_data);
+
+    auto hashtree_desc =
+            GetHashtreeDescriptor(out_avb_partition_name, std::move(*system_other_vbmeta));
+    EXPECT_NE(nullptr, hashtree_desc);
+}
+
+TEST(AvbHandleTest, LoadAndVerifyVbmeta_SystemOther) {
+    // Non-A/B device doesn't have system_other partition.
+    if (fs_mgr_get_slot_suffix() == "") return;
+
+    // Skip running this test if system_other is a logical partition.
+    // Note that system_other is still a physical partition on "retrofit" devices.
+    if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+        !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+        return;
+    }
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+    // It should have two lines in the fstab, the first for logical system_other,
+    // the other for physical system_other.
+    EXPECT_EQ(2UL, fstab.size());
+
+    // Use the 2nd fstab entry, which is for physical system_other partition.
+    FstabEntry* system_other_entry = &fstab[1];
+    // Assign the default key if it's not specified in the fstab.
+    if (system_other_entry->avb_key.empty()) {
+        system_other_entry->avb_key = "/system/etc/security/avb/system_other.avbpubkey";
+    }
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(*system_other_entry);
+    EXPECT_NE(nullptr, avb_handle) << "Failed to load system_other vbmeta. Try 'adb root'?";
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+}
+
+TEST(AvbHandleTest, GetSecurityPatchLevel) {
+    Fstab fstab;
+    EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta();
+    EXPECT_NE(nullptr, avb_handle) << "Failed to load inline vbmeta. Try 'adb root'?";
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+    // Gets security patch level with format: YYYY-MM-DD (e.g., 2019-04-05).
+    FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+    EXPECT_NE(nullptr, system_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*system_entry).length());
+
+    FstabEntry* vendor_entry = GetEntryForMountPoint(&fstab, "/vendor");
+    EXPECT_NE(nullptr, vendor_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*vendor_entry).length());
+
+    FstabEntry* product_entry = GetEntryForMountPoint(&fstab, "/product");
+    EXPECT_NE(nullptr, product_entry);
+    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*product_entry).length());
+}
+
+}  // namespace fs_avb_device_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
index 2e46644..ab1980b 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
@@ -29,7 +29,7 @@
 #include <android-base/unique_fd.h>
 #include <base/files/file_path.h>
 #include <base/strings/stringprintf.h>
-#include <fs_avb/fs_avb.h>
+#include <fs_avb/types.h>
 #include <gtest/gtest.h>
 
 // Utility macro to run the command expressed by the printf()-style string
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
new file mode 100644
index 0000000..7c34009
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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/fs_avb_util.h>
+
+#include "fs_avb_test_util.h"
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbUtilTest : public BaseFsAvbTest {
+  public:
+    PublicFsAvbUtilTest(){};
+
+  protected:
+    ~PublicFsAvbUtilTest(){};
+};
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor) {
+    // Generates a raw system_other.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,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+    auto hashtree_desc =
+            GetHashtreeDescriptor("system" /* avb_partition_name */, std::move(system_vbmeta));
+    EXPECT_NE(nullptr, hashtree_desc);
+
+    // Checks the returned hashtree_desc matches the following info returned by avbtool.
+    EXPECT_EQ(
+            "Footer version:           1.0\n"
+            "Image size:               15728640 bytes\n"
+            "Original image size:      10485760 bytes\n"
+            "VBMeta offset:            10661888\n"
+            "VBMeta size:              2112 bytes\n"
+            "--\n"
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          1280 bytes\n"
+            "Algorithm:                SHA512_RSA4096\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:            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(system_path));
+
+    EXPECT_EQ(1UL, hashtree_desc->dm_verity_version);
+    EXPECT_EQ(10485760UL, hashtree_desc->image_size);
+    EXPECT_EQ(10485760UL, hashtree_desc->tree_offset);
+    EXPECT_EQ(86016UL, hashtree_desc->tree_size);
+    EXPECT_EQ(4096UL, hashtree_desc->data_block_size);
+    EXPECT_EQ(4096UL, hashtree_desc->hash_block_size);
+    EXPECT_EQ(2UL, hashtree_desc->fec_num_roots);
+    EXPECT_EQ(10571776UL, hashtree_desc->fec_offset);
+    EXPECT_EQ(90112UL, hashtree_desc->fec_size);
+    EXPECT_EQ(std::string("sha1"),
+              std::string(reinterpret_cast<const char*>(hashtree_desc->hash_algorithm)));
+    EXPECT_EQ(std::string("system").length(), hashtree_desc->partition_name_len);
+    EXPECT_EQ(hashtree_desc->partition_name, "system");
+    EXPECT_EQ(hashtree_desc->salt, "d00df00d");
+    EXPECT_EQ(hashtree_desc->root_digest, "a3d5dd307341393d85de356c384ff543ec1ed81b");
+
+    // Checks it's null if partition name doesn't match.
+    EXPECT_EQ(nullptr, GetHashtreeDescriptor("system_not_exist" /* avb_partition_name */,
+                                             std::move(system_vbmeta)));
+}
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor_NotFound) {
+    // 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);
+    // Appends AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+    auto boot_vbmeta = ExtractAndLoadVBMetaData(boot_path, "boot-vbmeta.img");
+
+    auto hashtree_desc =
+            GetHashtreeDescriptor("boot" /* avb_partition_name */, std::move(boot_vbmeta));
+    EXPECT_EQ(nullptr, hashtree_desc);
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/types.cpp b/fs_mgr/libfs_avb/types.cpp
new file mode 100644
index 0000000..3c277f3
--- /dev/null
+++ b/fs_mgr/libfs_avb/types.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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/types.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper functions to print enum class VBMetaVerifyResult.
+const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
+    // clang-format off
+    static const char* const name[] = {
+        "ResultSuccess",
+        "ResultError",
+        "ResultErrorVerification",
+        "ResultUnknown",
+    };
+    // clang-format on
+
+    uint32_t index = static_cast<uint32_t>(result);
+    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+    if (index >= unknown_index) {
+        index = unknown_index;
+    }
+
+    return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
+    os << VBMetaVerifyResultToString(result);
+    return os;
+}
+
+// Helper functions to dump enum class AvbHandleStatus.
+const char* AvbHandleStatusToString(AvbHandleStatus status) {
+    // clang-format off
+    static const char* const name[] = {
+        "Success",
+        "Uninitialized",
+        "HashtreeDisabled",
+        "VerificationDisabled",
+        "VerificationError",
+        "Unknown",
+    };
+    // clang-format on
+
+    uint32_t index = static_cast<uint32_t>(status);
+    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+    if (index >= unknown_index) {
+        index = unknown_index;
+    }
+
+    return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
+    os << AvbHandleStatusToString(status);
+    return os;
+}
+
+// class VBMetaData
+// ----------------
+std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
+    auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();
+
+    if (!vbmeta_header) return nullptr;
+
+    /* Byteswap the header. */
+    avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
+                                               vbmeta_header.get());
+    if (update_vbmeta_size) {
+        vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
+                       vbmeta_header->authentication_data_block_size +
+                       vbmeta_header->auxiliary_data_block_size;
+    }
+
+    return vbmeta_header;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index ea12e96..19737fe 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -14,6 +14,7 @@
 
 cc_test {
     name: "fs_mgr_unit_test",
+    test_suites: ["device-tests"],
 
     shared_libs: [
         "libbase",
@@ -23,9 +24,6 @@
         "libfs_mgr",
         "libfstab",
     ],
-    data: [
-        "data/*",
-    ],
     srcs: [
         "fs_mgr_test.cpp",
     ],
@@ -36,3 +34,32 @@
         "-Werror",
     ],
 }
+
+cc_prebuilt_binary {
+    name: "adb-remount-test.sh",
+    srcs: ["adb-remount-test.sh"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+        android: {
+            enabled: false,
+        },
+    },
+    host_supported: true,
+}
+
+java_test_host {
+    name: "fs_mgr_vendor_overlay_test",
+
+    srcs:  ["src/**/VendorOverlayHostTest.java"],
+
+    libs: ["tradefed"],
+
+    test_config: "vendor-overlay-test.xml",
+
+    test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 8298bf2..8a1b6d6 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -11,23 +11,24 @@
 ##  USAGE
 ##
 
-USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]
 
-adb remount tests (overlayfs focus)
+adb remount tests
+
+--help        This help
+--serial      Specify device (must if multiple are present)
+--color       Dress output with highlighting colors
+--print-time  Report the test duration
 
 Conditions:
  - Must be a userdebug build.
  - Must be in adb mode.
- - Kernel must have overlayfs enabled and patched to support override_creds.
- - Must have either squashfs, ext4-dedupe or right-sized partitions.
- - Minimum expectation system and vender are overlayfs covered partitions.
+ - Also tests overlayfs
+  - Kernel must have overlayfs enabled and patched to support override_creds.
+  - Must have either erofs, squashfs, ext4-dedupe or full partitions.
+  - Minimum expectation system and vender are overlayfs covered partitions.
 "
 
-if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
-  echo "${USAGE}" >&2
-  exit 0
-fi
-
 ##
 ##  Helper Variables
 ##
@@ -44,6 +45,9 @@
 ORANGE="${ESCAPE}[38;5;255:165:0m"
 BLUE="${ESCAPE}[35m"
 NORMAL="${ESCAPE}[0m"
+TMPDIR=${TMPDIR:-/tmp}
+print_time=false
+start_time=`date +%s`
 
 ##
 ##  Helper Functions
@@ -210,6 +214,21 @@
   fi
 }
 
+[ "USAGE: usb_status > stdout
+
+If adb_wait failed, check if device is in fastboot mode and report status
+
+Returns: \"(USB stack borken?)\", \"(In fastboot mode)\" or \"(in adb mode)\"" ]
+usb_status() {
+  if inFastboot; then
+    echo "(In fastboot mode)"
+  elif inAdb; then
+    echo "(In adb mode)"
+  else
+    echo "(USB stack borken?)"
+  fi
+}
+
 [ "USAGE: fastboot_wait [timeout]
 
 Returns: waits until the device has returned for fastboot or optional timeout" ]
@@ -234,11 +253,11 @@
 
 Returns: true if device in root state" ]
 adb_root() {
-  [ `adb_sh echo '${USER}'` != root ] || return 0
+  [ root != "`adb_sh echo '${USER}' </dev/null`" ] || return 0
   adb root >/dev/null </dev/null 2>/dev/null
   sleep 2
   adb_wait 2m &&
-    [ `adb_sh echo '${USER}'` = root ]
+    [ root = "`adb_sh echo '${USER}' </dev/null`" ]
 }
 
 [ "USAGE: adb_unroot
@@ -247,11 +266,11 @@
 
 Returns: true if device in un root state" ]
 adb_unroot() {
-  [ `adb_sh echo '${USER}'` = root ] || return 0
+  [ root = "`adb_sh echo '${USER}' </dev/null`" ] || return 0
   adb unroot >/dev/null </dev/null 2>/dev/null
   sleep 2
   adb_wait 2m &&
-    [ `adb_sh echo '${USER}'` != root ]
+    [ root != "`adb_sh echo '${USER}' </dev/null`" ]
 }
 
 [ "USAGE: fastboot_getvar var expected
@@ -278,13 +297,37 @@
   echo ${O} >&2
 }
 
+[ "USAGE: restore
+
+Do nothing: should be redefined when necessary.  Called after cleanup.
+
+Returns: reverses configurations" ]
+restore() {
+  true
+}
+
 [ "USAGE: cleanup
 
 Do nothing: should be redefined when necessary
 
-Returns: cleans up any latent resources, reverses configurations" ]
-cleanup () {
-  :
+Returns: cleans up any latent resources" ]
+cleanup() {
+  true
+}
+
+[ "USAGE: test_duration >/dev/stderr
+
+Prints the duration of the test
+
+Returns: reports duration" ]
+test_duration() {
+  if ${print_time}; then
+    echo "${BLUE}[     INFO ]${NORMAL} end `date`"
+    [ -n "${start_time}" ] || return
+    end_time=`date +%s`
+    diff_time=`expr ${end_time} - ${start_time}`
+    echo "${BLUE}[     INFO ]${NORMAL} duration `format_duration ${diff_time}`"
+  fi >&2
 }
 
 [ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
@@ -306,6 +349,8 @@
   fi
   echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
   cleanup
+  restore
+  test_duration
   exit 1
 }
 
@@ -383,13 +428,73 @@
     -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
 }
 
+[ "USAGE: skip_unrelated_mounts < /proc/mounts
+
+or output from df
+
+Filters out all apex and vendor override administrative overlay mounts
+uninteresting to the test" ]
+skip_unrelated_mounts() {
+    grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
+      grep -v "[%] /\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+}
+
 ##
 ##  MAINLINE
 ##
 
-if [ X"-s" = X"${1}" -a -n "${2}" ]; then
-  export ANDROID_SERIAL="${2}"
-  shift 2
+OPTIONS=`getopt --alternative --unquoted \
+                --longoptions help,serial:,colour,color,no-colour,no-color \
+                --longoptions gtest_print_time,print-time \
+                -- "?hs:" ${*}` ||
+  ( echo "${USAGE}" >&2 ; false ) ||
+  die "getopt failure"
+set -- ${OPTIONS}
+
+color=false
+while [ ${#} -gt 0 ]; do
+  case ${1} in
+    -h | --help | -\?)
+      echo "${USAGE}" >&2
+      exit 0
+      ;;
+    -s | --serial)
+      export ANDROID_SERIAL=${2}
+      shift
+      ;;
+    --color | --colour)
+      color=true
+      ;;
+    --no-color | --no-colour)
+      color=false
+      ;;
+    --print-time | --gtest_print_time)
+      print_time=true
+      ;;
+    --)
+      shift
+      break
+      ;;
+    -*)
+      echo "${USAGE}" >&2
+      die "${0}: error unknown option ${1}"
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
+if ! ${color}; then
+  GREEN=""
+  RED=""
+  ORANGE=""
+  BLUE=""
+  NORMAL=""
+fi
+
+if ${print_time}; then
+  echo "${BLUE}[     INFO ]${NORMAL}" start `date` >&2
 fi
 
 inFastboot && die "device in fastboot mode"
@@ -425,15 +530,43 @@
 [ -z "${BUILD_DESCRIPTION}" ] ||
   echo "${BLUE}[     INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
 
-VERITY_WAS_ENABLED=false
+# Report existing partition sizes
+adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+  sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
+  while read name device; do
+    case ${name} in
+      system_[ab] | system | vendor_[ab] | vendor | super | cache)
+        case ${device} in
+          sd*)
+            device=${device%%[0-9]*}/${device}
+            ;;
+        esac
+        size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+          size=`expr ${size} / 2` &&
+          echo "${BLUE}[     INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+        ;;
+    esac
+  done
+
+# Can we test remount -R command?
+overlayfs_supported=true
 if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
      "2" = "`get_property partition.system.verified`" ]; then
-  VERITY_WAS_ENABLED=true
+  restore() {
+    ${overlayfs_supported} || return 0
+    inFastboot &&
+      fastboot reboot &&
+      adb_wait 2m
+    inAdb &&
+      adb_root &&
+      adb enable-verity >/dev/null 2>/dev/null &&
+      adb_reboot &&
+      adb_wait 2m
+  }
 fi
 
 echo "${GREEN}[ RUN      ]${NORMAL} Testing kernel support for overlayfs" >&2
 
-overlayfs_supported=true;
 adb_wait || die "wait for device failed"
 adb_sh ls -d /sys/module/overlay </dev/null >/dev/null 2>/dev/null ||
   adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
@@ -452,7 +585,7 @@
           echo "${ORANGE}[  WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
           false
         ) ||
-        overlayfs_supported=false;
+        overlayfs_supported=false
       ;;
     *)
       echo "${GREEN}[       OK ]${NORMAL} overlay module uses callers creds" >&2
@@ -484,9 +617,9 @@
   echo "${ORANGE}[  WARNING ]${NORMAL} rebooting before test" >&2
   adb_reboot &&
     adb_wait 2m ||
-    die "lost device after reboot after wipe (USB stack broken?)"
+    die "lost device after reboot after wipe `usb_status`"
   adb_root ||
-    die "lost device after elevation to root after wipe (USB stack broken?)"
+    die "lost device after elevation to root after wipe `usb_status`"
 fi
 D=`adb_sh df -k </dev/null` &&
   H=`echo "${D}" | head -1` &&
@@ -505,11 +638,12 @@
 D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
 no_dedupe=true
 for d in ${D}; do
-  adb_sh tune2fs -l $d 2>&1 |
+  adb_sh tune2fs -l $d </dev/null 2>&1 |
     grep "Filesystem features:.*shared_blocks" >/dev/null &&
   no_dedupe=false
 done
-D=`adb_sh df -k ${D} </dev/null`
+D=`adb_sh df -k ${D} </dev/null |
+   sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
 echo "${D}"
 if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
   overlayfs_needed=false
@@ -523,8 +657,8 @@
 H=`adb disable-verity 2>&1`
 err=${?}
 L=
-D="${H%?Now reboot your device for settings to take effect}"
-if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+D="${H%?Now reboot your device for settings to take effect*}"
+if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
   echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
 fi
 if [ ${err} != 0 ]; then
@@ -548,16 +682,16 @@
   L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
   adb_reboot &&
     adb_wait 2m ||
-    die "lost device after reboot requested (USB stack broken?)"
+    die "lost device after reboot requested `usb_status`"
   adb_root ||
-    die "lost device after elevation to root (USB stack broken?)"
+    die "lost device after elevation to root `usb_status`"
   rebooted=true
   # re-disable verity to see the setup remarks expected
   T=`adb_date`
   H=`adb disable-verity 2>&1`
   err=${?}
-  D="${H%?Now reboot your device for settings to take effect}"
-  if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+  D="${H%?Now reboot your device for settings to take effect*}"
+  if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
     echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
   fi
   if [ ${err} != 0 ]; then
@@ -588,12 +722,16 @@
 
 echo "${GREEN}[ RUN      ]${NORMAL} remount" >&2
 
-adb remount ||
+D=`adb remount 2>&1`
+ret=${?}
+echo "${D}"
+[ ${ret} != 0 ] ||
+  [ X"${D}" = X"${D##*remount failed}" ] ||
   ( [ -n "${L}" ] && echo "${L}" && false ) ||
   die -t "${T}" "adb remount failed"
 D=`adb_sh df -k </dev/null` &&
   H=`echo "${D}" | head -1` &&
-  D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
+  D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` ||
   ( [ -n "${L}" ] && echo "${L}" && false )
 ret=${?}
 uses_dynamic_scratch=false
@@ -637,11 +775,34 @@
     echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
     die  "overlay takeover after remount"
   !(adb_sh grep "^overlay " /proc/mounts </dev/null |
-    grep -v "^overlay /\(vendor\|system\|bionic\)/..* overlay ro," |
-    grep " overlay ro,") &&
-    !(adb_sh grep " rw," /proc/mounts </dev/null |
-      skip_administrative_mounts data) ||
+    skip_unrelated_mounts |
+    grep " overlay ro,") ||
     die "remount overlayfs missed a spot (ro)"
+  D=`adb_sh grep " rw," /proc/mounts </dev/null |
+     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 | sort -u`
+  bad_rw=false
+  for d in ${D}; do
+    if adb_sh tune2fs -l $d </dev/null 2>&1 |
+       grep "Filesystem features:.*shared_blocks" >/dev/null; then
+      bad_rw=true
+    else
+      d=`adb_sh df -k ${D} </dev/null |
+       sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
+      [ X"${d}" = X"${d##* 100[%] }" ] ||
+        bad_rw=true
+    fi
+  done
+  [ -z "${D}" ] ||
+    D=`adb_sh df -k ${D} </dev/null |
+       sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
+           -e 's/^Filesystem      /Filesystem (rw) /'`
+  [ -z "${D}" ] || echo "${D}"
+  ${bad_rw} && die "remount overlayfs missed a spot (rw)"
 else
   if [ ${ret} = 0 ]; then
     die -t ${T} "unexpected overlay takeover"
@@ -694,7 +855,7 @@
 
   adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
     skip_administrative_mounts |
-    grep -v ' \(squashfs\|ext4\|f2fs\) ' &&
+    grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
     echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
     echo "${GREEN}[       OK ]${NORMAL} overlay takeover in first stage init" >&2
 fi
@@ -722,7 +883,10 @@
   adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
   die "pull libc.so from device"
 diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
-rm -r ${tempdir}
+rm -rf ${tempdir}
+cleanup() {
+  true
+}
 echo "${GREEN}[       OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
 
 echo "${GREEN}[ RUN      ]${NORMAL} flash vendor, confirm its content disappears" >&2
@@ -735,10 +899,14 @@
   echo "${ORANGE}[  WARNING ]${NORMAL} vendor image missing, skipping"
 elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} wrong vendor image, skipping"
+elif [ -z "${ANDROID_HOST_OUT}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} please run lunch, skipping"
 else
-  adb reboot-fastboot &&
-    fastboot_wait 2m &&
-    fastboot flash vendor ||
+  adb reboot-fastboot ||
+    die "fastbootd not supported (wrong adb in path?)"
+  fastboot_wait 2m ||
+    die "reboot into fastboot to flash vendor `usb_status`"
+  fastboot flash vendor ||
     ( fastboot reboot && false) ||
     die "fastboot flash vendor"
   fastboot_getvar is-userspace yes &&
@@ -777,12 +945,12 @@
     die "can not reboot out of fastboot"
   echo "${ORANGE}[  WARNING ]${NORMAL} adb after fastboot"
   adb_wait 2m ||
-    die "did not reboot after flash"
+    die "did not reboot after flash `usb_status`"
   if ${overlayfs_needed}; then
     adb_root &&
       D=`adb_sh df -k </dev/null` &&
       H=`echo "${D}" | head -1` &&
-      D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
+      D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
       echo "${H}" &&
       echo "${D}" &&
       echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
@@ -820,7 +988,7 @@
 H=`adb remount 2>&1`
 err=${?}
 L=
-D="${H%?Now reboot your device for settings to take effect}"
+D="${H%?Now reboot your device for settings to take effect*}"
 if [ X"${H}" != X"${D}" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
   L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
@@ -851,17 +1019,18 @@
 
   adb reboot-fastboot ||
     die "Reboot into fastbootd"
+  img=${TMPDIR}/adb-remount-test-${$}.img
   cleanup() {
-    rm /tmp/adb-remount-test.img
+    rm ${img}
   }
-  dd if=/dev/zero of=/tmp/adb-remount-test.img bs=4096 count=16 2>/dev/null &&
+  dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
     fastboot_wait 2m ||
-    die "reboot into fastboot"
-  fastboot flash --force ${scratch_partition} /tmp/adb-remount-test.img
+    die "reboot into fastboot `usb_status`"
+  fastboot flash --force ${scratch_partition} ${img}
   err=${?}
   cleanup
   cleanup() {
-    :
+    true
   }
   fastboot reboot ||
     die "can not reboot out of fastboot"
@@ -873,14 +1042,32 @@
   T=`adb_date`
   D=`adb disable-verity 2>&1`
   err=${?}
-  adb remount ||
-    die "remount failed"
+  if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
+  then
+    echo "${ORANGE}[  WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+    adb_reboot &&
+      adb_wait 2m &&
+      adb_root ||
+      die "failed to reboot"
+    T=`adb_date`
+    D="${D}
+`adb disable-verity 2>&1`"
+    err=${?}
+  fi
+
   echo "${D}"
   [ ${err} = 0 ] &&
     [ X"${D}" = X"${D##*setup failed}" ] &&
-    [ X"${D}" != X"${D##*using overlayfs}" ] &&
+    [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
     echo "${GREEN}[       OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
     die -t ${T} "setup for overlayfs"
+  D=`adb remount 2>&1`
+  err=${?}
+  echo "${D}"
+  [ ${err} != 0 ] ||
+    [ X"${D}" = X"${D##*remount failed}" ] ||
+    ( echo "${D}" && false ) ||
+    die -t ${T} "remount failed"
 fi
 
 echo "${GREEN}[ RUN      ]${NORMAL} test raw remount commands" >&2
@@ -888,21 +1075,25 @@
 # Prerequisite is a prepped device from above.
 adb_reboot &&
   adb_wait 2m ||
-  die "lost device after reboot to ro state (USB stack broken?)"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null &&
+  die "lost device after reboot to ro state `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
   die "/vendor is not read-only"
-adb_su mount -o rw,remount /vendor ||
+adb_su mount -o rw,remount /vendor </dev/null ||
   die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null ||
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
   die "/vendor is not read-write"
 echo "${GREEN}[       OK ]${NORMAL} mount -o rw,remount command works" >&2
 
-if $VERITY_WAS_ENABLED && $overlayfs_supported; then
-  adb_root &&
-    adb enable-verity &&
-    adb_reboot &&
-    adb_wait 2m ||
-    die "failed to restore verity" >&2
-fi
+restore
+err=${?}
+
+restore() {
+  true
+}
+
+[ ${err} = 0 ] ||
+  die "failed to restore verity" >&2
 
 echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
+
+test_duration
diff --git a/fs_mgr/tests/data/fstab.example b/fs_mgr/tests/data/fstab.example
deleted file mode 100644
index aebce32..0000000
--- a/fs_mgr/tests/data/fstab.example
+++ /dev/null
@@ -1,15 +0,0 @@
-# Android fstab file.
-
-#<src>                                              <mnt_point>        <type>      <mnt_flags and options>                               <fs_mgr_flags>
-
-/dev/block/bootdevice/by-name/system                /                  ext4        ro,barrier=1                                          wait,slotselect,avb
-/dev/block/bootdevice/by-name/metadata              /metadata          ext4        noatime,nosuid,nodev,discard                          wait,formattable
-/dev/block/bootdevice/by-name/userdata              /data              f2fs        noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier       latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
-/dev/block/bootdevice/by-name/misc                  /misc              emmc        defaults                                              defaults
-/dev/block/bootdevice/by-name/modem                 /vendor/firmware_mnt          vfat        ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0   wait,slotselect
-/devices/platform/soc/a600000.ssusb/a600000.dwc3*   auto               vfat        defaults                                              voldmanaged=usb:auto
-/dev/block/zram0                                    none               swap        defaults                                              zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none2              swap        nodiratime,remount,bind                               zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none3              swap        unbindable,private,slave                              zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none4              swap        noexec,shared,rec                                     zramsize=1073741824,max_comp_streams=8
-/dev/block/zram0                                    none5              swap        rw                                                    zramsize=1073741824,max_comp_streams=8
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1815a38..6afc8d2 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -203,10 +203,32 @@
     EXPECT_EQ(i, fstab.size());
 }
 
-TEST(fs_mgr, ReadFstabFromFile_MountOptions) {
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_MountOptions) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source /            ext4    ro,barrier=1                    wait,slotselect,avb
+source /metadata    ext4    noatime,nosuid,nodev,discard    wait,formattable
+
+source /data        f2fs    noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier    latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
+
+source /misc        emmc    defaults                        defaults
+
+source /vendor/firmware_mnt    vfat    ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0    wait,slotselect
+
+source auto         vfat    defaults                        voldmanaged=usb:auto
+source none         swap    defaults                        zramsize=1073741824,max_comp_streams=8
+source none2        swap    nodiratime,remount,bind         zramsize=1073741824,max_comp_streams=8
+source none3        swap    unbindable,private,slave        zramsize=1073741824,max_comp_streams=8
+source none4        swap    noexec,shared,rec               zramsize=1073741824,max_comp_streams=8
+source none5        swap    rw                              zramsize=1073741824,max_comp_streams=8
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
     Fstab fstab;
-    std::string fstab_file = android::base::GetExecutableDirectory() + "/data/fstab.example";
-    EXPECT_TRUE(ReadFstabFromFile(fstab_file, &fstab));
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(11U, fstab.size());
 
     EXPECT_EQ("/", fstab[0].mount_point);
     EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[0].flags);
@@ -286,7 +308,8 @@
     // clang-format on
 }
 
-TEST(fs_mgr, ReadFstabFromFile_FsMgrFlags) {
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_FsMgrFlags) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
@@ -297,7 +320,7 @@
 source none4       swap   defaults      checkpoint=fs
 source none5       swap   defaults      defaults
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -376,7 +399,7 @@
 source none2       swap   defaults      forcefdeorfbe=
 
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -456,7 +479,7 @@
     std::string fstab_contents = R"fs(
 source none0       swap   defaults      encryptable=/dir/key
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -480,7 +503,7 @@
 source none2       swap   defaults      voldmanaged=sdcard:3
 source none3       swap   defaults      voldmanaged=sdcard:auto
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -521,7 +544,7 @@
 source none0       swap   defaults      length=blah
 source none1       swap   defaults      length=123456
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -547,7 +570,7 @@
 source none0       swap   defaults      swapprio=blah
 source none1       swap   defaults      swapprio=123456
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -577,7 +600,7 @@
 source none4       swap   defaults      zramsize=105%
 source none5       swap   defaults      zramsize=%
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -623,7 +646,7 @@
 source none0       swap   defaults      verify=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -646,7 +669,7 @@
 source none0       swap   defaults      forceencrypt=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -669,7 +692,7 @@
 source none0       swap   defaults      forcefdeorfbe=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -704,7 +727,7 @@
 source none10      swap   defaults      fileencryption=ice:adiantum:
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -787,7 +810,7 @@
 source none0       swap   defaults      max_comp_streams=blah
 source none1       swap   defaults      max_comp_streams=123456
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -815,7 +838,7 @@
 source none2       swap   defaults      reservedsize=1K
 source none3       swap   defaults      reservedsize=2m
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -853,7 +876,7 @@
 source none2       swap   defaults      eraseblk=5000
 source none3       swap   defaults      eraseblk=8192
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -891,7 +914,7 @@
 source none2       swap   defaults      logicalblk=5000
 source none3       swap   defaults      logicalblk=8192
 )fs";
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -927,7 +950,7 @@
 source none0       swap   defaults      avb=vbmeta_partition
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -950,7 +973,7 @@
 source none0       swap   defaults      keydirectory=/dir/key
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -972,7 +995,7 @@
 source none0       swap   defaults      sysfs_path=/sys/device
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
@@ -1002,7 +1025,7 @@
 
 )fs";
 
-    ASSERT_TRUE(android::base::WriteStringToFd(fstab_contents, tf.fd));
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+  boolean wasRoot = false;
+
+  @Before
+  public void setup() throws DeviceNotAvailableException {
+    wasRoot = getDevice().isAdbRoot();
+    if (!wasRoot) {
+      Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+    }
+
+    Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+        testConditionsMet());
+
+    getDevice().remountSystemWritable();
+    // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+    Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+    Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+    CommandResult result = getDevice().executeShellV2Command("df");
+    Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+        vendorPattern.matcher(result.getStdout()).find());
+    Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+        productPattern.matcher(result.getStdout()).find());
+  }
+
+  private boolean cmdSucceeded(CommandResult result) {
+    return result.getStatus() == CommandStatus.SUCCESS;
+  }
+
+  private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+    CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+    Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+  }
+
+  /**
+   * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+   */
+  @Test
+  public void testVendorOverlay() throws DeviceNotAvailableException {
+    String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+    // Create files and modify policy
+    CommandResult result = getDevice().executeShellV2Command(
+        "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+        "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+    Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+    assumeMkdirSuccess("/vendor/testdir");
+    assumeMkdirSuccess("/vendor/diffcontext");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    result = getDevice().executeShellV2Command(
+        "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+    Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+    result = getDevice().executeShellV2Command(
+        "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+    getDevice().reboot();
+
+    // Test that the file was overlaid properly
+    result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+    Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+    Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+    Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+  }
+
+  // Duplicate of fs_mgr_overlayfs_valid() logic
+  // Requires root
+  public boolean testConditionsMet() throws DeviceNotAvailableException {
+    if (cmdSucceeded(getDevice().executeShellV2Command(
+        "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+      return true;
+    }
+    if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+      return false;
+    }
+    CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+    Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+    Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+    kernelVersionMatcher.find();
+    int majorKernelVersion;
+    int minorKernelVersion;
+    try {
+      majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+      minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+    } catch (Exception e) {
+      return false;
+    }
+    if (majorKernelVersion < 4) {
+      return true;
+    }
+    if (majorKernelVersion > 4) {
+      return false;
+    }
+    if (minorKernelVersion > 6) {
+      return false;
+    }
+    return true;
+  }
+
+  @After
+  public void tearDown() throws DeviceNotAvailableException {
+    if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+      getDevice().reboot();
+    }
+    if (!wasRoot) {
+      getDevice().disableAdbRoot();
+    }
+  }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for vendor overlay test cases">
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+    </test>
+</configuration>
+
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1b077bc..6e55c11a 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -47,6 +47,7 @@
 using android::base::Split;
 using android::base::Timer;
 using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
 using android::fs_mgr::AvbHashtreeResult;
 using android::fs_mgr::AvbUniquePtr;
 using android::fs_mgr::BuildGsiSystemFstabEntry;
@@ -188,6 +189,29 @@
     return true;
 }
 
+static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta,
+                                      const AvbHandle& standalone_vbmeta,
+                                      const FstabEntry& fstab_entry) {
+    std::string old_spl = builtin_vbmeta.GetSecurityPatchLevel(fstab_entry);
+    std::string new_spl = standalone_vbmeta.GetSecurityPatchLevel(fstab_entry);
+
+    bool rollbacked = false;
+    if (old_spl.empty() || new_spl.empty() || new_spl < old_spl) {
+        rollbacked = true;
+    }
+
+    if (rollbacked) {
+        LOG(ERROR) << "Image rollback detected for " << fstab_entry.mount_point
+                   << ", SPL switches from '" << old_spl << "' to '" << new_spl << "'";
+        if (AvbHandle::IsDeviceUnlocked()) {
+            LOG(INFO) << "Allowing rollbacked standalone image when the device is unlocked";
+            return false;
+        }
+    }
+
+    return rollbacked;
+}
+
 // Class Definitions
 // -----------------
 FirstStageMount::FirstStageMount(Fstab fstab)
@@ -737,8 +761,25 @@
         hashtree_result =
                 avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
     } else if (!fstab_entry->avb_key.empty()) {
-        hashtree_result =
-                AvbHandle::SetUpStandaloneAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+        if (!InitAvbHandle()) return false;
+        // Checks if hashtree should be disabled from the top-level /vbmeta.
+        if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
+            avb_handle_->status() == AvbHandleStatus::kVerificationDisabled) {
+            LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for "
+                       << fstab_entry->mount_point;
+            return true;  // Returns true to mount the partition directly.
+        } else {
+            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+            if (!avb_standalone_handle) {
+                LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
+                return false;
+            }
+            if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
+                return false;
+            }
+            hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
+                    fstab_entry, false /* wait_for_verity_dev */);
+        }
     } else {
         return true;  // No need AVB, returns true to mount the partition directly.
     }
@@ -754,8 +795,6 @@
         default:
             return false;
     }
-
-    return true;  // Returns true to mount the partition.
 }
 
 bool FirstStageMountVBootV2::InitAvbHandle() {
diff --git a/init/init.cpp b/init/init.cpp
index a8924f2..bbef1a9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -42,6 +42,7 @@
 #include <fs_mgr_vendor_overlay.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
+#include <libgsi/libgsi.h>
 #include <processgroup/processgroup.h>
 #include <selinux/android.h>
 
@@ -695,6 +696,13 @@
     // Nexus 9 boot time, so it's disabled by default.
     if (false) DumpState();
 
+    // Make the GSI status available before scripts start running.
+    if (android::gsi::IsGsiRunning()) {
+        property_set("ro.gsid.image_running", "1");
+    } else {
+        property_set("ro.gsid.image_running", "0");
+    }
+
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
 
     am.QueueEventTrigger("early-init");
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 46e5e12..4bc857a 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,6 +39,7 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
+#include <map>
 #include <memory>
 #include <queue>
 #include <vector>
@@ -442,8 +443,8 @@
 }
 
 // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr, std::string* error) {
+uint32_t CheckPermissions(const std::string& name, const std::string& value,
+                          const std::string& source_context, const ucred& cr, std::string* error) {
     if (!IsLegalPropertyName(name)) {
         *error = "Illegal property name";
         return PROP_ERROR_INVALID_NAME;
@@ -456,7 +457,6 @@
             return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
         }
 
-        HandleControlMessage(name.c_str() + 4, value, cr.pid);
         return PROP_SUCCESS;
     }
 
@@ -475,6 +475,21 @@
         return PROP_ERROR_INVALID_VALUE;
     }
 
+    return PROP_SUCCESS;
+}
+
+// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+                           const std::string& source_context, const ucred& cr, std::string* error) {
+    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
+        return ret;
+    }
+
+    if (StartsWith(name, "ctl.")) {
+        HandleControlMessage(name.c_str() + 4, value, cr.pid);
+        return PROP_SUCCESS;
+    }
+
     // sys.powerctl is a special property that is used to make the device reboot.  We want to log
     // any process that sets this property to be able to accurately blame the cause of a shutdown.
     if (name == "sys.powerctl") {
@@ -579,13 +594,15 @@
     }
 }
 
-static bool load_properties_from_file(const char *, const char *);
+static bool load_properties_from_file(const char*, const char*,
+                                      std::map<std::string, std::string>*);
 
 /*
  * Filter is used to decide which properties to load: NULL loads all keys,
  * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
  */
-static void LoadProperties(char* data, const char* filter, const char* filename) {
+static void LoadProperties(char* data, const char* filter, const char* filename,
+                           std::map<std::string, std::string>* properties) {
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
@@ -624,7 +641,7 @@
                 while (isspace(*key)) key++;
             }
 
-            load_properties_from_file(fn, key);
+            load_properties_from_file(fn, key, properties);
 
         } else {
             value = strchr(key, '=');
@@ -651,12 +668,19 @@
                 continue;
             }
 
-            uint32_t result = 0;
             ucred cr = {.pid = 1, .uid = 0, .gid = 0};
             std::string error;
-            result = HandlePropertySet(key, value, context, cr, &error);
-            if (result != PROP_SUCCESS) {
-                LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+            if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {
+                auto it = properties->find(key);
+                if (it == properties->end()) {
+                    (*properties)[key] = value;
+                } else if (it->second != value) {
+                    LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
+                                 << it->second << "' with new value '" << value << "'";
+                    it->second = value;
+                }
+            } else {
+                LOG(ERROR) << "Do not have permissions to set '" << key << "' to '" << value
                            << "' in property file '" << filename << "': " << error;
             }
         }
@@ -665,7 +689,8 @@
 
 // Filter is used to decide which properties to load: NULL loads all keys,
 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter) {
+static bool load_properties_from_file(const char* filename, const char* filter,
+                                      std::map<std::string, std::string>* properties) {
     Timer t;
     auto file_contents = ReadFile(filename);
     if (!file_contents) {
@@ -675,7 +700,7 @@
     }
     file_contents->push_back('\n');
 
-    LoadProperties(file_contents->data(), filter, filename);
+    LoadProperties(file_contents->data(), filter, filename, properties);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
     return true;
 }
@@ -698,7 +723,15 @@
 
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
-        load_properties_from_file("/data/local.prop", NULL);
+        std::map<std::string, std::string> properties;
+        load_properties_from_file("/data/local.prop", nullptr, &properties);
+        for (const auto& [name, value] : properties) {
+            std::string error;
+            if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+                LOG(ERROR) << "Could not set '" << name << "' to '" << value
+                           << "' in /data/local.prop: " << error;
+            }
+        }
     }
 }
 
@@ -835,24 +868,33 @@
 
 void property_load_boot_defaults() {
     // TODO(b/117892318): merge prop.default and build.prop files into one
-    // TODO(b/122864654): read the prop files from all partitions and then
-    // resolve the duplication by their origin so that RO and non-RO properties
-    // have a consistent overriding order.
-    if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
+    // We read the properties and their values into a map, in order to always allow properties
+    // loaded in the later property files to override the properties in loaded in the earlier
+    // property files, regardless of if they are "ro." properties or not.
+    std::map<std::string, std::string> properties;
+    if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
         // Try recovery path
-        if (!load_properties_from_file("/prop.default", NULL)) {
+        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
             // Try legacy path
-            load_properties_from_file("/default.prop", NULL);
+            load_properties_from_file("/default.prop", nullptr, &properties);
         }
     }
-    load_properties_from_file("/product/build.prop", NULL);
-    load_properties_from_file("/product_services/build.prop", NULL);
-    load_properties_from_file("/odm/default.prop", NULL);
-    load_properties_from_file("/vendor/default.prop", NULL);
-    load_properties_from_file("/system/build.prop", NULL);
-    load_properties_from_file("/odm/build.prop", NULL);
-    load_properties_from_file("/vendor/build.prop", NULL);
-    load_properties_from_file("/factory/factory.prop", "ro.*");
+    load_properties_from_file("/system/build.prop", nullptr, &properties);
+    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+    load_properties_from_file("/odm/default.prop", nullptr, &properties);
+    load_properties_from_file("/odm/build.prop", nullptr, &properties);
+    load_properties_from_file("/product/build.prop", nullptr, &properties);
+    load_properties_from_file("/product_services/build.prop", nullptr, &properties);
+    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
+
+    for (const auto& [name, value] : properties) {
+        std::string error;
+        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+            LOG(ERROR) << "Could not set '" << name << "' to '" << value
+                       << "' while loading .prop files" << error;
+        }
+    }
 
     property_initialize_ro_product_props();
     property_derive_build_fingerprint();
@@ -908,6 +950,13 @@
             LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                      &property_infos);
         }
+        if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
+                                     &property_infos);
+        }
+        if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
+        }
     } else {
         if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
             return;
@@ -916,6 +965,8 @@
             // Fallback to nonplat_* if vendor_* doesn't exist.
             LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
         }
+        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
+        LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 4ab439d..0fd2a3a 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
     name: "libasyncio",
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
+    double_loadable: true,
     recovery_available: true,
     host_supported: true,
     srcs: [
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 6a967f7..781819a 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -46,8 +46,7 @@
   }
 }
 
-BacktraceMap::~BacktraceMap() {
-}
+BacktraceMap::~BacktraceMap() {}
 
 void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
   ScopedBacktraceMapIteratorLock lock(this);
@@ -68,12 +67,13 @@
   char permissions[5];
   int name_pos;
 
-// Mac OS vmmap(1) output:
-// __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW  /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0         1         2         3         4         5
-  if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c  %n",
-             &start, &end, permissions, &name_pos) != 3) {
+  // Mac OS vmmap(1) output:
+  // __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW
+  // /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+  // 012345678901234567890123456789012345678901234567890123456789
+  // 0         1         2         3         4         5
+  if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c  %n", &start, &end,
+             permissions, &name_pos) != 3) {
     return false;
   }
 
@@ -90,21 +90,21 @@
     map->flags |= PROT_EXEC;
   }
 
-  map->name = line+name_pos;
-  if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
-    map->name.erase(map->name.length()-1);
+  map->name = line + name_pos;
+  if (!map->name.empty() && map->name[map->name.length() - 1] == '\n') {
+    map->name.erase(map->name.length() - 1);
   }
 
-  ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
-        reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
-        map->flags, map->name.c_str());
+  ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", reinterpret_cast<void*>(map->start),
+        reinterpret_cast<void*>(map->end), map->flags, map->name.c_str());
   return true;
 }
 #endif  // defined(__APPLE__)
 
 bool BacktraceMap::Build() {
 #if defined(__APPLE__)
-  char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
+  char
+      cmd[sizeof(pid_t) * 3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
   char line[1024];
   // cmd is guaranteed to always be big enough to hold this string.
   snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
@@ -113,7 +113,7 @@
     return false;
   }
 
-  while(fgets(line, sizeof(line), fp)) {
+  while (fgets(line, sizeof(line), fp)) {
     backtrace_map_t map;
     if (ParseLine(line, &map)) {
       maps_.push_back(map);
@@ -123,7 +123,7 @@
   return true;
 #else
   return android::procinfo::ReadProcessMaps(
-      pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) {
+      pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t, const char* name) {
         maps_.resize(maps_.size() + 1);
         backtrace_map_t& map = maps_.back();
         map.start = start;
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index c9580af..e3da77b 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -80,7 +80,7 @@
 // Determine whether application-level tracing is enabled for this process.
 static bool atrace_is_app_tracing_enabled()
 {
-    bool sys_debuggable = __android_log_is_debuggable();
+    bool sys_debuggable = property_get_bool("ro.debuggable", 0);
     bool result = false;
 
     if (sys_debuggable || atrace_is_debuggable) {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 1d4a0a0..9b41ebe 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -46,6 +46,8 @@
     vendor_available: true,
     recovery_available: true,
     export_include_dirs: ["include"],
+    system_shared_libs: [],
+    stl: "none",
     target: {
         windows: {
             enabled: true,
@@ -99,6 +101,11 @@
     header_libs: ["liblog_headers"],
     export_header_lib_headers: ["liblog_headers"],
 
+    stubs: {
+        symbol_file: "liblog.map.txt",
+        versions: ["10000"],
+    },
+
     cflags: [
         "-Werror",
         // This is what we want to do:
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 191ef1b..ce4c53c 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -19,12 +19,12 @@
     android_logger_get_log_readable_size; # vndk
     android_logger_get_log_version; # vndk
     android_logger_get_log_size; # vndk
-    android_logger_list_alloc; # vndk
-    android_logger_list_alloc_time; # vndk
-    android_logger_list_free; # vndk
+    android_logger_list_alloc; # apex vndk
+    android_logger_list_alloc_time; # apex vndk
+    android_logger_list_free; # apex vndk
     android_logger_list_open; # vndk
-    android_logger_list_read; # vndk
-    android_logger_open; # vndk
+    android_logger_list_read; # apex vndk
+    android_logger_open; # apex vndk
     android_logger_set_log_size; # vndk
 };
 
@@ -33,18 +33,18 @@
     android_logger_get_prune_list; # vndk
     android_logger_set_prune_list; # vndk
     android_logger_get_statistics; # vndk
-    __android_log_error_write; # vndk
+    __android_log_error_write; # apex vndk
     __android_log_is_loggable;
-    create_android_logger; #vndk
-    android_log_destroy; #vndk
-    android_log_write_list_begin; #vndk
-    android_log_write_list_end; #vndk
-    android_log_write_int32; #vndk
-    android_log_write_int64; #vndk
-    android_log_write_string8; #vndk
-    android_log_write_string8_len; #vndk
-    android_log_write_float32; #vndk
-    android_log_write_list; #vndk
+    create_android_logger; # apex vndk
+    android_log_destroy; # apex vndk
+    android_log_write_list_begin; # apex vndk
+    android_log_write_list_end; # apex vndk
+    android_log_write_int32; # apex vndk
+    android_log_write_int64; # apex vndk
+    android_log_write_string8; # apex vndk
+    android_log_write_string8_len; # apex vndk
+    android_log_write_float32; # apex vndk
+    android_log_write_list; # apex vndk
 
 };
 
@@ -56,19 +56,19 @@
 
 LIBLOG_Q {
   global:
+    __android_log_bswrite; # apex
+    __android_log_btwrite; # apex
+    __android_log_bwrite; # apex
+    __android_log_close; # apex
+    __android_log_security; # apex
     android_log_reset; #vndk
     android_log_parser_reset; #vndk
 };
 
 LIBLOG_PRIVATE {
   global:
-    __android_log_bswrite;
-    __android_log_btwrite;
-    __android_log_bwrite;
-    __android_log_close;
     __android_log_pmsg_file_read;
     __android_log_pmsg_file_write;
-    __android_log_security;
     __android_log_security_bswrite;
     __android_logger_get_buffer_size;
     __android_logger_property_get_bool;
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 2f0af4a..b7ba782 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -100,148 +100,29 @@
   return -EBADF;
 }
 
-/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
+// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
+// O_CLOEXEC is always set.
+static int socket_local_client(const std::string& name, int type) {
+  sockaddr_un addr = {.sun_family = AF_LOCAL};
 
-#if defined(_WIN32)
-
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId, int type) {
-  errno = ENOSYS;
-  return -ENOSYS;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
-/* Private copy of ../libcutils/socket_local.h prevent library loops */
-#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
-#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
-/* End of ../libcutils/socket_local.h */
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-LIBLOG_WEAK int socket_make_sockaddr_un(const char* name, int namespaceId,
-                                        struct sockaddr_un* p_addr, socklen_t* alen) {
-  memset(p_addr, 0, sizeof(*p_addr));
-  size_t namelen;
-
-  switch (namespaceId) {
-    case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
-#if defined(__linux__)
-      namelen = strlen(name);
-
-      /* Test with length +1 for the *initial* '\0'. */
-      if ((namelen + 1) > sizeof(p_addr->sun_path)) {
-        goto error;
-      }
-
-      /*
-       * Note: The path in this case is *not* supposed to be
-       * '\0'-terminated. ("man 7 unix" for the gory details.)
-       */
-
-      p_addr->sun_path[0] = 0;
-      memcpy(p_addr->sun_path + 1, name, namelen);
-#else
-      /* this OS doesn't have the Linux abstract namespace */
-
-      namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
-      /* unix_path_max appears to be missing on linux */
-      if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
-        goto error;
-      }
-
-      strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
-      strcat(p_addr->sun_path, name);
-#endif
-      break;
-
-    case ANDROID_SOCKET_NAMESPACE_RESERVED:
-      namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
-      /* unix_path_max appears to be missing on linux */
-      if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
-        goto error;
-      }
-
-      strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
-      strcat(p_addr->sun_path, name);
-      break;
-
-    case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
-      namelen = strlen(name);
-      /* unix_path_max appears to be missing on linux */
-      if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
-        goto error;
-      }
-
-      strcpy(p_addr->sun_path, name);
-      break;
-
-    default:
-      /* invalid namespace id */
-      return -1;
+  std::string path = "/dev/socket/" + name;
+  if (path.size() + 1 > sizeof(addr.sun_path)) {
+    return -1;
   }
+  strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
 
-  p_addr->sun_family = AF_LOCAL;
-  *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
-  return 0;
-error:
-  return -1;
-}
-
-/**
- * connect to peer named "name" on fd
- * returns same fd or -1 on error.
- * fd is not closed on error. that's your job.
- *
- * Used by AndroidSocketImpl
- */
-LIBLOG_WEAK int socket_local_client_connect(int fd, const char* name, int namespaceId,
-                                            int type __unused) {
-  struct sockaddr_un addr;
-  socklen_t alen;
-  int err;
-
-  err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
-  if (err < 0) {
-    goto error;
-  }
-
-  if (connect(fd, (struct sockaddr*)&addr, alen) < 0) {
-    goto error;
-  }
-
-  return fd;
-
-error:
-  return -1;
-}
-
-/**
- * connect to peer named "name"
- * returns fd or -1 on error
- */
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId, int type) {
-  int s;
-
-  s = socket(AF_LOCAL, type, 0);
-  if (s < 0) return -1;
-
-  if (0 > socket_local_client_connect(s, name, namespaceId, type)) {
-    close(s);
+  int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
+  if (fd == 0) {
     return -1;
   }
 
-  return s;
-}
+  if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+    close(fd);
+    return -1;
+  }
 
-#endif /* !_WIN32 */
-/* End of ../libcutils/socket_local_client.c */
+  return fd;
+}
 
 /* worker for sending the command to the logger */
 static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
@@ -250,7 +131,7 @@
   size_t len;
   char* cp;
   int errno_save = 0;
-  int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+  int sock = socket_local_client("logd", SOCK_STREAM);
   if (sock < 0) {
     return sock;
   }
@@ -460,10 +341,10 @@
     return sock;
   }
 
-  sock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+  sock = socket_local_client("logdr", SOCK_SEQPACKET);
   if (sock == 0) {
     /* Guarantee not file descriptor zero */
-    int newsock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+    int newsock = socket_local_client("logdr", SOCK_SEQPACKET);
     close(sock);
     sock = newsock;
   }
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 4ab2acb..50755ce 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -31,6 +31,7 @@
     shared_libs: [
         "libm",
         "libbase",
+        "libcutils",
     ],
     static_libs: ["liblog"],
     srcs: ["liblog_benchmark.cpp"],
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 6d78ed6..3534eb8 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -23,7 +23,7 @@
 #ifdef __ANDROID__
 #ifndef NO_PSTORE
   FILE* fp;
-  ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
+  ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "ae")));
   static const char message[] = "libc.__pstore_append\n";
   ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
   int fflushReturn = fflush(fp);
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index c8ac321..7d11ccf 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -172,7 +172,7 @@
  * Measure the time it takes to submit the android logging data to pstore
  */
 static void BM_pmsg_short(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -248,7 +248,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_short_aligned(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -323,7 +323,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_short_unaligned1(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -398,7 +398,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_long_aligned(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -471,7 +471,7 @@
  * best case aligned single block.
  */
 static void BM_pmsg_long_unaligned1(benchmark::State& state) {
-  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+  int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
   if (pstore_fd < 0) {
     state.SkipWithError("/dev/pmsg0");
     return;
@@ -949,8 +949,8 @@
 
 // Must be functionally identical to liblog internal __send_log_msg.
 static void send_to_control(char* buf, size_t len) {
-  int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                 SOCK_STREAM);
+  int sock =
+      socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
   if (sock < 0) return;
   size_t writeLen = strlen(buf) + 1;
 
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 83f0fa9..1f87b3e 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -93,7 +93,7 @@
 static std::string popenToString(const std::string& command) {
   std::string ret;
 
-  FILE* fp = popen(command.c_str(), "r");
+  FILE* fp = popen(command.c_str(), "re");
   if (fp) {
     if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
     pclose(fp);
@@ -645,7 +645,7 @@
   char buffer[512];
   snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid);
 
-  FILE* fp = fopen(buffer, "r");
+  FILE* fp = fopen(buffer, "re");
   if (!fp) {
     return;
   }
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
index f202c67..fa1255e 100644
--- a/liblog/tests/log_radio_test.cpp
+++ b/liblog/tests/log_radio_test.cpp
@@ -91,7 +91,7 @@
       "logcat -b radio --pid=%u -d -s"
       " TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
       (unsigned)getpid());
-  FILE* fp = popen(buf.c_str(), "r");
+  FILE* fp = popen(buf.c_str(), "re");
   int count = 0;
   int count_false = 0;
   if (fp) {
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
index 0656c0b..13f026d 100644
--- a/liblog/tests/log_system_test.cpp
+++ b/liblog/tests/log_system_test.cpp
@@ -91,7 +91,7 @@
       "logcat -b system --pid=%u -d -s"
       " TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
       (unsigned)getpid());
-  FILE* fp = popen(buf.c_str(), "r");
+  FILE* fp = popen(buf.c_str(), "re");
   int count = 0;
   int count_false = 0;
   if (fp) {
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo.cpp b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
index 439cf68..9fb22a1 100644
--- a/libmeminfo/libdmabufinfo/dmabufinfo.cpp
+++ b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
@@ -150,18 +150,14 @@
         auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
                                 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
         if (buf != dmabufs->end()) {
-            if (buf->name() == "" || buf->name() == "<unknown>")
-                buf->SetName(name);
-            if (buf->exporter() == "" || buf->exporter() == "<unknown>")
-                buf->SetExporter(exporter);
-            if (buf->count() == 0)
-                buf->SetCount(count);
+            if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
+            if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
+            if (buf->count() == 0) buf->SetCount(count);
             buf->AddFdRef(pid);
             continue;
         }
 
-        DmaBuffer& db =
-                dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
+        DmaBuffer& db = dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
         db.AddFdRef(pid);
     }
 
@@ -182,29 +178,12 @@
     // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
     // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
     auto account_dmabuf = [&](uint64_t start, uint64_t end, uint16_t /* flags */,
-                              uint64_t /* pgoff */, const char* name) {
+                              uint64_t /* pgoff */, ino_t inode, const char* name) {
         // no need to look into this mapping if it is not dmabuf
         if (!FileIsDmaBuf(std::string(name))) {
             return;
         }
 
-        // TODO (b/123532375) : Add inode number to the callback of ReadMapFileContent.
-        //
-        // Workaround: we know 'name' points to the name at the end of 'line'.
-        // We use that to backtrack and pick up the inode number from the line as well.
-        // start    end      flag pgoff    mj:mn inode   name
-        // 00400000-00409000 r-xp 00000000 00:00 426998  /dmabuf (deleted)
-        const char* p = name;
-        p--;
-        // skip spaces
-        while (p != line && *p == ' ') {
-            p--;
-        }
-        // walk backwards to the beginning of inode number
-        while (p != line && isdigit(*p)) {
-            p--;
-        }
-        uint64_t inode = strtoull(p, nullptr, 10);
         auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
                                 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
         if (buf != dmabufs->end()) {
diff --git a/libmeminfo/libdmabufinfo/tools/Android.bp b/libmeminfo/libdmabufinfo/tools/Android.bp
index 0af3c48..224b68e 100644
--- a/libmeminfo/libdmabufinfo/tools/Android.bp
+++ b/libmeminfo/libdmabufinfo/tools/Android.bp
@@ -26,5 +26,5 @@
     static_libs: [
         "libdmabufinfo",
     ],
-    soc_specific: true,
+    product_specific: true,
 }
\ No newline at end of file
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index 069b6b3..934d65c 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -246,7 +246,7 @@
     // parse and read /proc/<pid>/maps
     std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
     if (!::android::procinfo::ReadMapFile(
-                maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
                                const char* name) {
                     maps_.emplace_back(Vma(start, end, pgoff, flags, name));
                 })) {
@@ -394,7 +394,7 @@
         // If it has, we are looking for the vma stats
         // 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
         if (!::android::procinfo::ReadMapFileContent(
-                    line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                    line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
                               const char* name) {
                         vma.start = start;
                         vma.end = end;
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 4e4554a..320b851 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -6,6 +6,7 @@
     vndk: {
         enabled: true,
     },
+    double_loadable: true,
     srcs: ["memtrack.cpp"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 701ce16..8e1be4c 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -18,6 +18,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <string.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
@@ -30,7 +31,8 @@
 struct ReadMapCallback {
   ReadMapCallback(allocator::vector<Mapping>& mappings) : mappings_(mappings) {}
 
-  void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) const {
+  void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t,
+                  const char* name) const {
     mappings_.emplace_back(start, end, flags & PROT_READ, flags & PROT_WRITE, flags & PROT_EXEC,
                            name);
   }
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index c54570e..10d42e4 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,5 +1,18 @@
+cc_defaults {
+    name: "libnativebridge-defaults",
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    cppflags: [
+        "-fvisibility=protected",
+    ],
+    header_libs: ["libnativebridge-headers"],
+    export_header_lib_headers: ["libnativebridge-headers"],
+}
+
 cc_library_headers {
-    name: "libnativebridge-dummy-headers",
+    name: "libnativebridge-headers",
 
     host_supported: true,
     export_include_dirs: ["include"],
@@ -7,6 +20,7 @@
 
 cc_library {
     name: "libnativebridge",
+    defaults: ["libnativebridge-defaults"],
 
     host_supported: true,
     srcs: ["native_bridge.cc"],
@@ -16,6 +30,8 @@
     shared_libs: [
         "liblog",
     ],
+    // TODO(jiyong): remove this line after aosp/885921 lands
+    export_include_dirs: ["include"],
 
     target: {
         android: {
@@ -30,16 +46,16 @@
         symbol_file: "libnativebridge.map.txt",
         versions: ["1"],
     },
+}
 
-    export_include_dirs: ["include"],
+// TODO(b/124250621): eliminate the need for this library
+cc_library {
+    name: "libnativebridge_lazy",
+    defaults: ["libnativebridge-defaults"],
 
-    cflags: [
-        "-Werror",
-        "-Wall",
-    ],
-    cppflags: [
-        "-fvisibility=protected",
-    ],
+    host_supported: false,
+    srcs: ["native_bridge_lazy.cc"],
+    required: ["libnativebridge"],
 }
 
 subdirs = ["tests"]
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
index 6f0824b..daf87f4 100644
--- a/libnativebridge/OWNERS
+++ b/libnativebridge/OWNERS
@@ -1,2 +1,4 @@
 dimitry@google.com
 eaeltsin@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/libnativebridge/native_bridge_lazy.cc b/libnativebridge/native_bridge_lazy.cc
new file mode 100644
index 0000000..94c8084
--- /dev/null
+++ b/libnativebridge/native_bridge_lazy.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "nativebridge/native_bridge.h"
+#define LOG_TAG "nativebridge"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+  static void* handle = dlopen("libnativebridge.so", RTLD_NOW);
+  LOG_FATAL_IF(handle == nullptr, "Failed to load libnativebridge.so: %s", dlerror());
+  return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+  auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+  LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+  return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+}  // namespace
+
+bool LoadNativeBridge(const char* native_bridge_library_filename,
+                      const struct NativeBridgeRuntimeCallbacks* runtime_callbacks) {
+  static auto f = GET_FUNC_PTR(LoadNativeBridge);
+  return f(native_bridge_library_filename, runtime_callbacks);
+}
+
+bool NeedsNativeBridge(const char* instruction_set) {
+  static auto f = GET_FUNC_PTR(NeedsNativeBridge);
+  return f(instruction_set);
+}
+
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set) {
+  static auto f = GET_FUNC_PTR(PreInitializeNativeBridge);
+  return f(app_data_dir, instruction_set);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+  static auto f = GET_FUNC_PTR(InitializeNativeBridge);
+  return f(env, instruction_set);
+}
+
+void UnloadNativeBridge() {
+  static auto f = GET_FUNC_PTR(UnloadNativeBridge);
+  return f();
+}
+
+bool NativeBridgeAvailable() {
+  static auto f = GET_FUNC_PTR(NativeBridgeAvailable);
+  return f();
+}
+
+bool NativeBridgeInitialized() {
+  static auto f = GET_FUNC_PTR(NativeBridgeInitialized);
+  return f();
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+  static auto f = GET_FUNC_PTR(NativeBridgeLoadLibrary);
+  return f(libpath, flag);
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetTrampoline);
+  return f(handle, name, shorty, len);
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+  static auto f = GET_FUNC_PTR(NativeBridgeIsSupported);
+  return f(libpath);
+}
+
+uint32_t NativeBridgeGetVersion() {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetVersion);
+  return f();
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetSignalHandler);
+  return f(signal);
+}
+
+bool NativeBridgeError() {
+  static auto f = GET_FUNC_PTR(NativeBridgeError);
+  return f();
+}
+
+bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename) {
+  static auto f = GET_FUNC_PTR(NativeBridgeNameAcceptable);
+  return f(native_bridge_library_filename);
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+  static auto f = GET_FUNC_PTR(NativeBridgeUnloadLibrary);
+  return f(handle);
+}
+
+const char* NativeBridgeGetError() {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetError);
+  return f();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+  static auto f = GET_FUNC_PTR(NativeBridgeIsPathSupported);
+  return f(path);
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+                                        const char* anon_ns_library_path) {
+  static auto f = GET_FUNC_PTR(NativeBridgeInitAnonymousNamespace);
+  return f(public_ns_sonames, anon_ns_library_path);
+}
+
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+    const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+    const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns) {
+  static auto f = GET_FUNC_PTR(NativeBridgeCreateNamespace);
+  return f(name, ld_library_path, default_library_path, type, permitted_when_isolated_path,
+           parent_ns);
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+                                struct native_bridge_namespace_t* to,
+                                const char* shared_libs_sonames) {
+  static auto f = GET_FUNC_PTR(NativeBridgeLinkNamespaces);
+  return f(from, to, shared_libs_sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+                                 struct native_bridge_namespace_t* ns) {
+  static auto f = GET_FUNC_PTR(NativeBridgeLoadLibraryExt);
+  return f(libpath, flag, ns);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+  static auto f = GET_FUNC_PTR(NativeBridgeGetVendorNamespace);
+  return f();
+}
+
+#undef GET_FUNC_PTR
+
+}  // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 744a4a8..2bb8467 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,7 +23,7 @@
         "-Wextra",
         "-Werror",
     ],
-    header_libs: ["libnativebridge-dummy-headers"],
+    header_libs: ["libnativebridge-headers"],
     cppflags: ["-fvisibility=protected"],
 }
 
@@ -46,9 +46,8 @@
 }
 
 // Build the unit tests.
-cc_test {
-    name: "libnativebridge-tests",
-    host_supported: true,
+cc_defaults {
+    name: "libnativebridge-tests-defaults",
     test_per_src: true,
 
     cflags: [
@@ -81,12 +80,24 @@
 
     shared_libs: [
         "liblog",
-        "libnativebridge",
         "libnativebridge-dummy",
     ],
     header_libs: ["libbase_headers"],
 }
 
+cc_test {
+    name: "libnativebridge-tests",
+    defaults: ["libnativebridge-tests-defaults"],
+    host_supported: true,
+    shared_libs: ["libnativebridge"],
+}
+
+cc_test {
+    name: "libnativebridge-lazy-tests",
+    defaults: ["libnativebridge-tests-defaults"],
+    shared_libs: ["libnativebridge_lazy"],
+}
+
 // Build the test for the C API.
 cc_test {
     name: "libnativebridge-api-tests",
@@ -95,5 +106,5 @@
     srcs: [
         "NativeBridgeApi.c",
     ],
-    header_libs: ["libnativebridge-dummy-headers"],
+    header_libs: ["libnativebridge-headers"],
 }
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 1ec21e9..b9f0dbf 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -1,16 +1,7 @@
 // Shared library for target
 // ========================================================
-cc_library {
-    name: "libnativeloader",
-    host_supported: true,
-    srcs: ["native_loader.cpp"],
-    shared_libs: [
-        "libnativehelper",
-        "liblog",
-        "libcutils",
-        "libnativebridge",
-        "libbase",
-    ],
+cc_defaults {
+    name: "libnativeloader-defaults",
     cflags: [
         "-Werror",
         "-Wall",
@@ -18,7 +9,21 @@
     cppflags: [
         "-fvisibility=hidden",
     ],
-    export_include_dirs: ["include"],
+    header_libs: ["libnativeloader-headers"],
+    export_header_lib_headers: ["libnativeloader-headers"],
+}
+
+cc_library {
+    name: "libnativeloader",
+    defaults: ["libnativeloader-defaults"],
+    host_supported: true,
+    srcs: ["native_loader.cpp"],
+    shared_libs: [
+        "libnativehelper",
+        "liblog",
+        "libnativebridge",
+        "libbase",
+    ],
     required: [
         "llndk.libraries.txt",
         "vndksp.libraries.txt",
@@ -29,9 +34,25 @@
     },
 }
 
+// TODO(b/124250621) eliminate the need for this library
+cc_library {
+    name: "libnativeloader_lazy",
+    defaults: ["libnativeloader-defaults"],
+    host_supported: false,
+    srcs: ["native_loader_lazy.cpp"],
+    required: ["libnativeloader"],
+}
+
+cc_library_headers {
+    name: "libnativeloader-headers",
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
+
+// TODO(jiyong) Remove this when its use in the internal master is
+// switched to libnativeloader-headers
 cc_library_headers {
     name: "libnativeloader-dummy-headers",
-
     host_supported: true,
     export_include_dirs: ["include"],
 }
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5394d7e..043f038 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -21,7 +21,6 @@
 #ifdef __ANDROID__
 #define LOG_TAG "libnativeloader"
 #include "nativeloader/dlext_namespaces.h"
-#include "cutils/properties.h"
 #include "log/log.h"
 #endif
 #include <dirent.h>
@@ -116,6 +115,8 @@
 
 static constexpr const char* kVndkNamespaceName = "vndk";
 
+static constexpr const char* kDefaultNamespaceName = "default";
+static constexpr const char* kPlatformNamespaceName = "platform";
 static constexpr const char* kRuntimeNamespaceName = "runtime";
 
 // classloader-namespace is a linker namespace that is created for the loaded
@@ -137,10 +138,18 @@
 
 static constexpr const char* kApexPath = "/apex/";
 
+#if defined(__LP64__)
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+#else
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+#endif
+
 static bool is_debuggable() {
-  char debuggable[PROP_VALUE_MAX];
-  property_get("ro.debuggable", debuggable, "0");
-  return std::string(debuggable) == "1";
+  bool debuggable = false;
+#ifdef __BIONIC__
+  debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+#endif
+  return debuggable;
 }
 
 static std::string vndk_version_str() {
@@ -265,8 +274,19 @@
 
     NativeLoaderNamespace native_loader_ns;
     if (!is_native_bridge) {
-      android_namespace_t* android_parent_ns =
-          parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
+      android_namespace_t* android_parent_ns;
+      if (parent_ns != nullptr) {
+        android_parent_ns = parent_ns->get_android_ns();
+      } else {
+        // Fall back to the platform namespace if no parent is found. It is
+        // called "default" for binaries in /system and "platform" for those in
+        // the Runtime APEX. Try "platform" first since "default" always exists.
+        android_parent_ns = android_get_exported_namespace(kPlatformNamespaceName);
+        if (android_parent_ns == nullptr) {
+          android_parent_ns = android_get_exported_namespace(kDefaultNamespaceName);
+        }
+      }
+
       android_namespace_t* ns = android_create_namespace(namespace_name,
                                                          nullptr,
                                                          library_path.c_str(),
@@ -315,8 +335,16 @@
 
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
-      native_bridge_namespace_t* native_bridge_parent_namespace =
-          parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
+      native_bridge_namespace_t* native_bridge_parent_namespace;
+      if (parent_ns != nullptr) {
+        native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
+      } else {
+        native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
+        if (native_bridge_parent_namespace == nullptr) {
+          native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
+        }
+      }
+
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
                                                                   nullptr,
                                                                   library_path.c_str(),
@@ -408,6 +436,14 @@
       }
     }
 
+    // Remove the public libs in the runtime namespace.
+    // These libs are listed in public.android.txt, but we don't want the rest of android
+    // in default namespace to dlopen the libs.
+    // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+    // Unfortunately, it does not have stable C symbols, and default namespace should only use
+    // stable symbols in libandroidicu.so. http://b/120786417
+    removePublicLibsIfExistsInRuntimeApex(sonames);
+
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
     //
@@ -502,6 +538,27 @@
     }
   }
 
+  /**
+   * Remove the public libs in runtime namespace
+   */
+  void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+    for (const std::string& lib_name : kRuntimePublicLibraries) {
+      std::string path(kRuntimeApexLibPath);
+      path.append("/").append(lib_name);
+
+      struct stat s;
+      // Do nothing if the path in /apex does not exist.
+      // Runtime APEX must be mounted since libnativeloader is in the same APEX
+      if (stat(path.c_str(), &s) != 0) {
+        continue;
+      }
+
+      auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+      if (it != sonames.end()) {
+        sonames.erase(it);
+      }
+    }
+  }
 
   bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
                   const std::function<bool(const std::string& /* soname */,
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
new file mode 100644
index 0000000..11ecc43
--- /dev/null
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "nativeloader/native_loader.h"
+#define LOG_TAG "nativeloader"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+  static void* handle = dlopen("libnativeloader.so", RTLD_NOW);
+  LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror());
+  return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+  auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+  LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+  return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+}  // namespace
+
+void InitializeNativeLoader() {
+  static auto f = GET_FUNC_PTR(InitializeNativeLoader);
+  return f();
+}
+
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+                                   bool is_shared, bool is_for_vendor, jstring library_path,
+                                   jstring permitted_path) {
+  static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
+  return f(env, target_sdk_version, class_loader, is_shared, is_for_vendor, library_path,
+           permitted_path);
+}
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+                        jobject class_loader, const char* caller_location, jstring library_path,
+                        bool* needs_native_bridge, char** error_msg) {
+  static auto f = GET_FUNC_PTR(OpenNativeLibrary);
+  return f(env, target_sdk_version, path, class_loader, caller_location, library_path,
+           needs_native_bridge, error_msg);
+}
+
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
+  static auto f = GET_FUNC_PTR(CloseNativeLibrary);
+  return f(handle, needs_native_bridge, error_msg);
+}
+
+void NativeLoaderFreeErrorMessage(char* msg) {
+  static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage);
+  return f(msg);
+}
+
+struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader);
+  return f(env, class_loader);
+}
+
+struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env,
+                                                                     jobject class_loader) {
+  static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader);
+  return f(env, class_loader);
+}
+
+void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path,
+                                   bool* needs_native_bridge, char** error_msg) {
+  static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace);
+  return f(ns, path, needs_native_bridge, error_msg);
+}
+
+void ResetNativeLoader() {
+  static auto f = GET_FUNC_PTR(ResetNativeLoader);
+  return f();
+}
+
+#undef GET_FUNC_PTR
+
+}  // namespace android
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index 1464e39..4d5c53d 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -78,5 +78,5 @@
     srcs: [
         "api_test.c",
     ],
-    header_libs: ["libnativeloader-dummy-headers"],
+    header_libs: ["libnativeloader-headers"],
 }
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 7f39e9b..4217a89 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -107,7 +107,7 @@
 
 // inline ARM implementations
 inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
-inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
     GGLfixed result, t;
     if (__builtin_constant_p(shift)) {
     asm("smull  %[lo], %[hi], %[x], %[y]            \n"
@@ -130,7 +130,8 @@
 }
 
 inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
-inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a,
+                                                          int shift) {
     GGLfixed result, t;
     if (__builtin_constant_p(shift)) {
     asm("smull  %[lo], %[hi], %[x], %[y]            \n"
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index cd8ef94..d094811 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -126,22 +126,26 @@
         return false;
     }
 
-    Json::Value cgroups = root["Cgroups"];
-    for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
-        std::string name = cgroups[i]["Controller"].asString();
-        descriptors->emplace(std::make_pair(
-                name,
-                CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
-                                 std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
-                                 cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString())));
+    if (root.isMember("Cgroups")) {
+        const Json::Value& cgroups = root["Cgroups"];
+        for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+            std::string name = cgroups[i]["Controller"].asString();
+            descriptors->emplace(std::make_pair(
+                    name,
+                    CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
+                                     std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+                                     cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString())));
+        }
     }
 
-    Json::Value cgroups2 = root["Cgroups2"];
-    descriptors->emplace(std::make_pair(
-            CGROUPV2_CONTROLLER_NAME,
-            CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
-                             std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
-                             cgroups2["UID"].asString(), cgroups2["GID"].asString())));
+    if (root.isMember("Cgroups2")) {
+        const Json::Value& cgroups2 = root["Cgroups2"];
+        descriptors->emplace(std::make_pair(
+                CGROUPV2_CONTROLLER_NAME,
+                CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+                                 std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+                                 cgroups2["UID"].asString(), cgroups2["GID"].asString())));
+    }
 
     return true;
 }
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index e9dec12..8505e61 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -200,7 +200,7 @@
         cgroups.push_back(path);
     }
     if (CgroupGetControllerPath("memory", &path)) {
-        cgroups.push_back(path);
+        cgroups.push_back(path + "/apps");
     }
 
     for (std::string cgroup_root_path : cgroups) {
@@ -317,6 +317,7 @@
 
     CgroupGetControllerPath("cpuacct", &cpuacct_path);
     CgroupGetControllerPath("memory", &memory_path);
+    memory_path += "/apps";
 
     const char* cgroup =
             (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
@@ -380,6 +381,7 @@
     std::string cgroup;
     if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
         CgroupGetControllerPath("memory", &cgroup);
+        cgroup += "/apps";
     } else {
         CgroupGetControllerPath("cpuacct", &cgroup);
     }
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
new file mode 100644
index 0000000..15d0172
--- /dev/null
+++ b/libprocessgroup/profiles/Android.bp
@@ -0,0 +1,80 @@
+// 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.
+
+prebuilt_etc {
+    name: "cgroups.json",
+    src: "cgroups.json",
+}
+
+prebuilt_etc {
+    name: "cgroups.recovery.json",
+    filename: "cgroups.json",
+    recovery: true,
+    src: "cgroups.recovery.json",
+}
+
+prebuilt_etc {
+    name: "task_profiles.json",
+    src: "task_profiles.json",
+}
+
+cc_library_static {
+    name: "libprocessgroup_proto",
+    host_supported: true,
+    srcs: [
+        "cgroups.proto",
+        "task_profiles.proto",
+    ],
+    proto: {
+        type: "full",
+        export_proto_headers: true,
+    },
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+}
+
+cc_test_host {
+    name: "libprocessgroup_proto_test",
+    srcs: [
+        "test.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libgmock",
+        "liblog",
+        "libjsoncpp",
+        "libjsonpbverify",
+        "libjsonpbparse",
+        "libprocessgroup_proto",
+    ],
+    shared_libs: [
+        "libprotobuf-cpp-full",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+    data: [
+        "cgroups.json",
+        "cgroups.recovery.json",
+        "task_profiles.json",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/libprocessgroup/profiles/TEST_MAPPING b/libprocessgroup/profiles/TEST_MAPPING
new file mode 100644
index 0000000..5ff4112
--- /dev/null
+++ b/libprocessgroup/profiles/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "libprocessgroup_proto_test",
+      "host": true
+    }
+  ]
+}
diff --git a/rootdir/cgroups.json b/libprocessgroup/profiles/cgroups.json
similarity index 100%
rename from rootdir/cgroups.json
rename to libprocessgroup/profiles/cgroups.json
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
new file mode 100644
index 0000000..f4070c5
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message Cgroups {
+    repeated Cgroup cgroups = 1 [json_name = "Cgroups"];
+    Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
+}
+
+// Next: 6
+message Cgroup {
+    string controller = 1 [json_name = "Controller"];
+    string path = 2 [json_name = "Path"];
+    string mode = 3 [json_name = "Mode"];
+    string uid = 4 [json_name = "UID"];
+    string gid = 5 [json_name = "GID"];
+}
+
+// Next: 5
+message Cgroups2 {
+    string path = 1 [json_name = "Path"];
+    string mode = 2 [json_name = "Mode"];
+    string uid = 3 [json_name = "UID"];
+    string gid = 4 [json_name = "GID"];
+}
diff --git a/libprocessgroup/profiles/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
new file mode 100644
index 0000000..f0bf5fd
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.recovery.json
@@ -0,0 +1,9 @@
+{
+  "Cgroups": [
+    {
+      "Controller": "cpuacct",
+      "Path": "/acct",
+      "Mode": "0555"
+    }
+  ]
+}
diff --git a/rootdir/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
similarity index 100%
rename from rootdir/task_profiles.json
rename to libprocessgroup/profiles/task_profiles.json
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
new file mode 100644
index 0000000..578f0d3
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message TaskProfiles {
+    repeated Attribute attributes = 1 [json_name = "Attributes"];
+    repeated Profile profiles = 2 [json_name = "Profiles"];
+}
+
+// Next: 4
+message Attribute {
+    string name = 1 [json_name = "Name"];
+    string controller = 2 [json_name = "Controller"];
+    string file = 3 [json_name = "File"];
+}
+
+// Next: 3
+message Profile {
+    string name = 1 [json_name = "Name"];
+    repeated Action actions = 2 [json_name = "Actions"];
+}
+
+// Next: 3
+message Action {
+    string name = 1 [json_name = "Name"];
+    map<string, string> params = 2 [json_name = "Params"];
+}
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
new file mode 100644
index 0000000..8ba14d6
--- /dev/null
+++ b/libprocessgroup/profiles/test.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 <string>
+
+#include <android-base/file.h>
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups.pb.h"
+#include "task_profiles.pb.h"
+
+using namespace ::android::jsonpb;
+using ::android::base::GetExecutableDirectory;
+using ::testing::MatchesRegex;
+
+namespace android {
+namespace profiles {
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+    return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
+}
+
+TEST(LibProcessgroupProto, EmptyMode) {
+    EXPECT_EQ(0, strtoul("", nullptr, 8))
+            << "Empty mode string cannot be silently converted to 0; this should not happen";
+}
+
+class CgroupsTest : public JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        cgroups_ = static_cast<Cgroups*>(message());
+    }
+    Cgroups* cgroups_;
+};
+
+TEST_P(CgroupsTest, CgroupRequiredFields) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        auto&& cgroup = cgroups_->cgroups(i);
+        EXPECT_FALSE(cgroup.controller().empty())
+                << "No controller name for cgroup #" << i << " in " << file_path_;
+        EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2RequiredFields) {
+    if (cgroups_->has_cgroups2()) {
+        EXPECT_FALSE(cgroups_->cgroups2().path().empty())
+                << "No path for cgroup2 in " << file_path_;
+    }
+}
+
+// "Mode" field must be in the format of "0xxx".
+static constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
+TEST_P(CgroupsTest, CgroupMode) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
+                << "For cgroup controller #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2Mode) {
+    EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
+            << "For cgroups2 in " << file_path_;
+}
+
+class TaskProfilesTest : public JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        task_profiles_ = static_cast<TaskProfiles*>(message());
+    }
+    TaskProfiles* task_profiles_;
+};
+
+TEST_P(TaskProfilesTest, AttributeRequiredFields) {
+    for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
+        auto&& attribute = task_profiles_->attributes(i);
+        EXPECT_FALSE(attribute.name().empty())
+                << "No name for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.controller().empty())
+                << "No controller for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.file().empty())
+                << "No file for attribute #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(TaskProfilesTest, ProfileRequiredFields) {
+    for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
+        auto&& profile = task_profiles_->profiles(profile_idx);
+        EXPECT_FALSE(profile.name().empty())
+                << "No name for profile #" << profile_idx << " in " << file_path_;
+        for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
+            auto&& action = profile.actions(action_idx);
+            EXPECT_FALSE(action.name().empty())
+                    << "No name for profiles[" << profile_idx << "].actions[" << action_idx
+                    << "] in " << file_path_;
+        }
+    }
+}
+
+// Test suite instantiations
+
+INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+                                           MakeTestParam<Cgroups>("/cgroups.recovery.json"),
+                                           MakeTestParam<TaskProfiles>("/task_profiles.json")));
+INSTANTIATE_TEST_SUITE_P(, CgroupsTest,
+                         ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+                                           MakeTestParam<Cgroups>("/cgroups.recovery.json")));
+INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>("/task_profiles.json")));
+
+}  // namespace profiles
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 447852d..b69103c 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -90,6 +90,10 @@
     if (sys_supports_timerslack) {
         auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
         if (!WriteStringToFile(std::to_string(slack_), file)) {
+            if (errno == ENOENT) {
+                // This happens when process is already dead
+                return true;
+            }
             PLOG(ERROR) << "set_timerslack_ns write failed";
         }
     }
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15f03d0..27cda93 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -31,6 +31,7 @@
     vndk: {
         enabled: true,
     },
+    double_loadable: true,
     host_supported: true,
     srcs: [
         "process.cpp",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 981241e..b6ec3cb 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -36,6 +36,7 @@
   uint64_t end_addr;
   uint16_t flags;
   uint64_t pgoff;
+  ino_t inode;
   char* next_line = content;
   char* p;
 
@@ -124,18 +125,25 @@
       return false;
     }
     // inode
-    if (!pass_xdigit() || (*p != '\0' && !pass_space())) {
+    inode = strtoull(p, &end, 10);
+    if (end == p) {
       return false;
     }
+    p = end;
+
+    if (*p != '\0' && !pass_space()) {
+      return false;
+    }
+
     // filename
-    callback(start_addr, end_addr, flags, pgoff, p);
+    callback(start_addr, end_addr, flags, pgoff, inode, p);
   }
   return true;
 }
 
-inline bool ReadMapFile(
-    const std::string& map_file,
-    const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadMapFile(const std::string& map_file,
+                        const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                 const char*)>& callback) {
   std::string content;
   if (!android::base::ReadFileToString(map_file, &content)) {
     return false;
@@ -143,9 +151,9 @@
   return ReadMapFileContent(&content[0], callback);
 }
 
-inline bool ReadProcessMaps(
-    pid_t pid,
-    const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadProcessMaps(pid_t pid,
+                            const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                     const char*)>& callback) {
   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
 }
 
@@ -154,17 +162,18 @@
   uint64_t end;
   uint16_t flags;
   uint64_t pgoff;
+  ino_t inode;
   std::string name;
 
-  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
-      : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
+  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
 };
 
 inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
   return ReadProcessMaps(
-      pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
-        maps->emplace_back(start, end, flags, pgoff, name);
-      });
+      pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+               const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
 }
 
 } /* namespace procinfo */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
index 04995d4..eba4fd0 100644
--- a/libprocinfo/process_map_benchmark.cpp
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -17,6 +17,7 @@
 #include <procinfo/process_map.h>
 
 #include <string.h>
+#include <sys/types.h>
 
 #include <string>
 
@@ -31,9 +32,10 @@
   std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
   for (auto _ : state) {
     std::vector<android::procinfo::MapInfo> maps;
-    android::procinfo::ReadMapFile(
-        map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
-                      const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
+    android::procinfo::ReadMapFile(map_file, [&](uint64_t start, uint64_t end, uint16_t flags,
+                                                 uint64_t pgoff, ino_t inode, const char* name) {
+      maps.emplace_back(start, end, flags, pgoff, inode, name);
+    });
     CHECK_EQ(maps.size(), 2043u);
   }
 }
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 170a806..562d864 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -26,23 +26,27 @@
   std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
   std::vector<android::procinfo::MapInfo> maps;
   ASSERT_TRUE(android::procinfo::ReadMapFile(
-      map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
-                    const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+      map_file,
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
   ASSERT_EQ(2043u, maps.size());
   ASSERT_EQ(maps[0].start, 0x12c00000ULL);
   ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
   ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
   ASSERT_EQ(maps[0].pgoff, 0ULL);
+  ASSERT_EQ(maps[0].inode, 10267643UL);
   ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
   ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
   ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
   ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
   ASSERT_EQ(maps[876].pgoff, 0ULL);
+  ASSERT_EQ(maps[876].inode, 2407UL);
   ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
   ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
   ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
   ASSERT_EQ(maps[1260].flags, PROT_READ);
   ASSERT_EQ(maps[1260].pgoff, 0ULL);
+  ASSERT_EQ(maps[1260].inode, 10266154UL);
   ASSERT_EQ(maps[1260].name,
             "[anon:dalvik-classes.dex extracted in memory from "
             "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
@@ -51,8 +55,9 @@
 TEST(process_map, ReadProcessMaps) {
   std::vector<android::procinfo::MapInfo> maps;
   ASSERT_TRUE(android::procinfo::ReadProcessMaps(
-      getpid(), [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
-                    const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+      getpid(),
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+          const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
   ASSERT_GT(maps.size(), 0u);
   maps.clear();
   ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
index 645baf4..4c99361 100644
--- a/libsysutils/include/sysutils/OWNERS
+++ b/libsysutils/include/sysutils/OWNERS
@@ -1 +1,2 @@
-per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
+include ../../src/OWNERS
+
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
index 645baf4..c65a40d 100644
--- a/libsysutils/src/OWNERS
+++ b/libsysutils/src/OWNERS
@@ -1 +1,2 @@
-per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
+per-file OWNERS,Netlink* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, satk@google.com
+
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index c90e383..1e4f72e 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -62,7 +62,7 @@
 bool Maps::Parse() {
   return android::procinfo::ReadMapFile(
       GetMapsFile(),
-      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
         // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
@@ -102,7 +102,7 @@
   std::string content(buffer_);
   return android::procinfo::ReadMapFileContent(
       &content[0],
-      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
         // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index fc6f305..39bf3a5 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -20,6 +20,7 @@
     vndk: {
         enabled: true,
     },
+    double_loadable: true,
     host_supported: true,
     srcs: ["usbhost.c"],
     cflags: ["-Werror"],
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index e73b366..bfa2508 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -10,6 +10,11 @@
     local_include_dirs: ["include/vndksupport"],
     export_include_dirs: ["include"],
     shared_libs: ["liblog"],
+    version_script: "libvndksupport.map.txt",
+    stubs: {
+        symbol_file: "libvndksupport.map.txt",
+        versions: ["29"],
+    },
 }
 
 llndk_library {
diff --git a/libvndksupport/include/vndksupport/linker.h b/libvndksupport/include/vndksupport/linker.h
index f509564..5f48c39 100644
--- a/libvndksupport/include/vndksupport/linker.h
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -20,6 +20,16 @@
 extern "C" {
 #endif
 
+/*
+ * Returns whether the current process is a vendor process.
+ *
+ * Note that this is only checking what process is running and has nothing to
+ * do with what namespace the caller is loaded at.  For example, a VNDK-SP
+ * library loaded by SP-HAL calling this function may still get a 'false',
+ * because it is running in a system process.
+ */
+int android_is_in_vendor_process();
+
 void* android_load_sphal_library(const char* name, int flag);
 
 int android_unload_sphal_library(void* handle);
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index 16e38da..ac9a99c 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,7 +1,8 @@
 LIBVNDKSUPPORT {
   global:
-    android_load_sphal_library; # vndk
-    android_unload_sphal_library; # vndk
+    android_is_in_vendor_process; # vndk apex
+    android_load_sphal_library; # vndk apex
+    android_unload_sphal_library; # vndk apex
   local:
     *;
 };
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index bc5620b..821940a 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -45,6 +45,17 @@
     return vendor_namespace;
 }
 
+int android_is_in_vendor_process() {
+    if (android_get_exported_namespace == NULL) {
+        ALOGD("android_get_exported_namespace() not available. Assuming system process.");
+        return 0;
+    }
+
+    // In vendor process, 'vndk' namespace is not visible, whereas in system
+    // process, it is.
+    return android_get_exported_namespace("vndk") == NULL;
+}
+
 void* android_load_sphal_library(const char* name, int flag) {
     struct android_namespace_t* vendor_namespace = get_vendor_namespace();
     if (vendor_namespace != NULL) {
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index e72d159..59377dd 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -50,7 +50,7 @@
 typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
 
 /* Get LMKD packet command */
-inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+static inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
     return (enum lmk_cmd)ntohl(pack[0]);
 }
 
@@ -64,8 +64,8 @@
  * For LMK_TARGET packet get target_idx-th payload.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
-                                 int target_idx, struct lmk_target *target) {
+static inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet, int target_idx,
+                                        struct lmk_target* target) {
     target->minfree = ntohl(packet[target_idx * 2 + 1]);
     target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
 }
@@ -74,9 +74,8 @@
  * Prepare LMK_TARGET packet and return packet size in bytes.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
-                                   struct lmk_target *targets,
-                                   size_t target_cnt) {
+static inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet, struct lmk_target* targets,
+                                          size_t target_cnt) {
     int idx = 0;
     packet[idx++] = htonl(LMK_TARGET);
     while (target_cnt) {
@@ -99,8 +98,7 @@
  * For LMK_PROCPRIO packet get its payload.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
-                                   struct lmk_procprio *params) {
+static inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet, struct lmk_procprio* params) {
     params->pid = (pid_t)ntohl(packet[1]);
     params->uid = (uid_t)ntohl(packet[2]);
     params->oomadj = ntohl(packet[3]);
@@ -110,8 +108,7 @@
  * Prepare LMK_PROCPRIO packet and return packet size in bytes.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
-                                   struct lmk_procprio *params) {
+static inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet, struct lmk_procprio* params) {
     packet[0] = htonl(LMK_PROCPRIO);
     packet[1] = htonl(params->pid);
     packet[2] = htonl(params->uid);
@@ -128,8 +125,8 @@
  * For LMK_PROCREMOVE packet get its payload.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
-                                   struct lmk_procremove *params) {
+static inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+                                            struct lmk_procremove* params) {
     params->pid = (pid_t)ntohl(packet[1]);
 }
 
@@ -137,8 +134,8 @@
  * Prepare LMK_PROCREMOVE packet and return packet size in bytes.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
-                                   struct lmk_procprio *params) {
+static inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+                                              struct lmk_procprio* params) {
     packet[0] = htonl(LMK_PROCREMOVE);
     packet[1] = htonl(params->pid);
     return 2 * sizeof(int);
@@ -148,7 +145,7 @@
  * Prepare LMK_PROCPURGE packet and return packet size in bytes.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+static inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
     packet[0] = htonl(LMK_PROCPURGE);
     return sizeof(int);
 }
@@ -163,8 +160,8 @@
  * For LMK_GETKILLCNT packet get its payload.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
-                                   struct lmk_getkillcnt *params) {
+static inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
+                                            struct lmk_getkillcnt* params) {
     params->min_oomadj = ntohl(packet[1]);
     params->max_oomadj = ntohl(packet[2]);
 }
@@ -173,8 +170,8 @@
  * Prepare LMK_GETKILLCNT packet and return packet size in bytes.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
-                                       struct lmk_getkillcnt *params) {
+static inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
+                                              struct lmk_getkillcnt* params) {
     packet[0] = htonl(LMK_GETKILLCNT);
     packet[1] = htonl(params->min_oomadj);
     packet[2] = htonl(params->max_oomadj);
@@ -185,7 +182,7 @@
  * Prepare LMK_GETKILLCNT reply packet and return packet size in bytes.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
+static inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
     packet[0] = htonl(LMK_GETKILLCNT);
     packet[1] = htonl(kill_cnt);
     return 2 * sizeof(int);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index cbbc710..8754fb5 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -42,33 +42,29 @@
 LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
 LOCAL_POST_INSTALL_CMD += ; ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
 
+# TODO(b/124106384): Clean up compat symlinks for ART binaries.
+ART_BINARIES= \
+  dalvikvm \
+  dalvikvm32 \
+  dalvikvm64 \
+  dex2oat \
+  dexdiag \
+  dexdump \
+  dexlist \
+  dexoptanalyzer \
+  oatdump \
+  profman \
+
+$(foreach b,$(ART_BINARIES), \
+  $(eval LOCAL_POST_INSTALL_CMD += \
+    ; ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+)
+
 # End of runtime APEX compatibilty.
 
 include $(BUILD_PREBUILT)
 
 #######################################
-# cgroups.json
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := cgroups.json
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# task_profiles.json
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := task_profiles.json
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-
-include $(BUILD_PREBUILT)
-
-#######################################
 # asan.options
 ifneq ($(filter address,$(SANITIZE_TARGET)),)
 
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
new file mode 100644
index 0000000..e4d3d5e
--- /dev/null
+++ b/rootdir/etc/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsBionicTestCases"
+    }
+  ]
+}
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 48ca998..0cde3f2 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -54,6 +54,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -87,6 +91,8 @@
 namespace.media.search.paths = /apex/com.android.media/${LIB}
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
 namespace.media.links = default
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.media.link.default.shared_libs += libandroid.so
@@ -110,6 +116,7 @@
 namespace.conscrypt.link.default.shared_libs  = libc.so
 namespace.conscrypt.link.default.shared_libs += libm.so
 namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -126,6 +133,8 @@
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 7aa097d..28703d2 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -20,13 +20,17 @@
 dir.vendor = /data/benchmarktest/vendor
 dir.vendor = /data/benchmarktest64/vendor
 
+dir.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
+
 dir.system = /data/nativetest
 dir.system = /data/nativetest64
 dir.system = /data/benchmarktest
 dir.system = /data/benchmarktest64
-# TODO(b/123864775): Ensure tests are run from one of the directories above and
-# remove this.
-dir.system = /data/local/tmp
 
 dir.postinstall = /postinstall
 
@@ -128,6 +132,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -161,6 +169,8 @@
 namespace.media.search.paths = /apex/com.android.media/${LIB}
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
 namespace.media.links = default
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.media.link.default.shared_libs += libandroid.so
@@ -184,6 +194,7 @@
 namespace.conscrypt.link.default.shared_libs  = libc.so
 namespace.conscrypt.link.default.shared_libs += libm.so
 namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -200,6 +211,8 @@
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
 # "sphal" namespace
@@ -408,6 +421,7 @@
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = system
 # TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
 # when it exists.
@@ -475,6 +489,116 @@
 namespace.system.link.runtime.shared_libs += libnativebridge.so
 namespace.system.link.runtime.shared_libs += libnativehelper.so
 namespace.system.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.system.link.runtime.shared_libs += libandroidicu.so
+
+###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime,resolv
+namespace.default.visible = true
+
+namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libjavacore.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 1904445..a5beb4e 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -20,6 +20,13 @@
 dir.vendor = /data/benchmarktest/vendor
 dir.vendor = /data/benchmarktest64/vendor
 
+dir.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
+
 dir.system = /data/nativetest
 dir.system = /data/nativetest64
 dir.system = /data/benchmarktest
@@ -72,6 +79,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -106,6 +117,8 @@
 namespace.media.search.paths = /apex/com.android.media/${LIB}
 namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
 
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
 namespace.media.links = default
 namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.media.link.default.shared_libs += libandroid.so
@@ -129,6 +142,7 @@
 namespace.conscrypt.link.default.shared_libs  = libc.so
 namespace.conscrypt.link.default.shared_libs += libm.so
 namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -145,6 +159,8 @@
 namespace.resolv.link.default.shared_libs += libm.so
 namespace.resolv.link.default.shared_libs += libdl.so
 namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
 # "sphal" namespace
@@ -305,11 +321,11 @@
 namespace.default.search.paths += /vendor/${LIB}/vndk-sp
 
 # Access to system libraries is allowed
-namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
@@ -323,8 +339,6 @@
 namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
 namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
-namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
 namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.asan.search.paths += /data/asan/system/${LIB}
@@ -333,12 +347,16 @@
 namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
 
 namespace.default.links = runtime
 namespace.default.link.runtime.shared_libs  = libdexfile_external.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 ###############################################################################
 # "runtime" APEX namespace
@@ -349,12 +367,118 @@
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
 # when it exists.
 namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime,resolv
+namespace.default.visible = true
+
+namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs  = libjavacore.so
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs  = libc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+
+###############################################################################
 # Namespace config for binaries under /postinstall.
 # Only default namespace is defined and default has no directories
 # other than /system/lib in the search paths. This is because linker calls
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index d10f7c1..d5665f2 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,5 +1,5 @@
 # set up the global environment
-on init
+on early-init
     export ANDROID_BOOTLOGO 1
     export ANDROID_ROOT /system
     export ANDROID_ASSETS /system/app
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 57032bc..1c84d15 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -538,7 +538,7 @@
     mkdir /data/apex/active 0750 root system
     mkdir /data/apex/backup 0700 root system
     mkdir /data/apex/sessions 0700 root system
-    mkdir /data/staging 0750 system system
+    mkdir /data/pkg_staging 0750 system system
 
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc
@@ -577,9 +577,6 @@
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
-    # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
-    exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
-
     # load fsverity keys
     exec -- /system/bin/mini-keyctl -c /product/etc/security/cacerts_fsverity,/vendor/etc/security/cacerts_fsverity -k .fs-verity
 
@@ -591,6 +588,9 @@
     setup_runtime_bionic
     parse_apex_configs
 
+    # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
+    exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
+
     # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
     # won't work.