Merge "android_filesystem_config add AID for iorapd"
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index c00d955..4cbc45a 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -408,7 +408,8 @@
     for (int i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
 
-        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+            android::base::EndsWithIgnoreCase(file, ".dm")) {
             struct stat sb;
             if (stat(file, &sb) != -1) total_size += sb.st_size;
             first_apk = i;
@@ -470,9 +471,9 @@
         }
 
         std::string cmd =
-                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
                                             install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
-                                            session_id, i, android::base::Basename(file).c_str());
+                                            session_id, android::base::Basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 6e143c1..e38e305 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -841,14 +841,19 @@
         return -1;
     }
 
-    std::string service = android::base::StringPrintf(
-        "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+    std::string service =
+            android::base::StringPrintf("sideload-host:%" PRId64 ":%d",
+                                        static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
     unique_fd device_fd(adb_connect(service, &error));
     if (device_fd < 0) {
-        // Try falling back to the older (<= K) sideload method. Maybe this
-        // is an older device that doesn't support sideload-host.
         fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+
+        // If this is a small enough package, maybe this is an older device that doesn't
+        // support sideload-host. Try falling back to the older (<= K) sideload method.
+        if (sb.st_size > INT_MAX) {
+            return -1;
+        }
         fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
         return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
     }
@@ -858,7 +863,7 @@
 
     char buf[SIDELOAD_HOST_BLOCK_SIZE];
 
-    size_t xfer = 0;
+    int64_t xfer = 0;
     int last_percent = -1;
     while (true) {
         if (!ReadFdExactly(device_fd, buf, 8)) {
@@ -874,20 +879,22 @@
             return 0;
         }
 
-        int block = strtol(buf, nullptr, 10);
-
-        size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
-        if (offset >= static_cast<size_t>(sb.st_size)) {
-            fprintf(stderr, "adb: failed to read block %d past end\n", block);
+        int64_t block = strtoll(buf, nullptr, 10);
+        int64_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
+        if (offset >= static_cast<int64_t>(sb.st_size)) {
+            fprintf(stderr,
+                    "adb: failed to read block %" PRId64 " at offset %" PRId64 ", past end %" PRId64
+                    "\n",
+                    block, offset, static_cast<int64_t>(sb.st_size));
             return -1;
         }
 
         size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
-        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
+        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<int64_t>(sb.st_size)) {
             to_write = sb.st_size - offset;
         }
 
-        if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
+        if (adb_lseek(package_fd, offset, SEEK_SET) != offset) {
             fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
             return -1;
         }
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 8c39a20..d55096a 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -32,6 +32,10 @@
 #include <unistd.h>
 #include <utime.h>
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -47,6 +51,7 @@
 #include "security_log_tags.h"
 #include "sysdeps/errno.h"
 
+using android::base::Dirname;
 using android::base::StringPrintf;
 
 static bool should_use_fs_config(const std::string& path) {
@@ -219,7 +224,7 @@
     }
 
     if (fd < 0 && errno == ENOENT) {
-        if (!secure_mkdirs(android::base::Dirname(path))) {
+        if (!secure_mkdirs(Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             goto fail;
         }
@@ -327,8 +332,6 @@
 #else
 static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
     syncmsg msg;
-    unsigned int len;
-    int ret;
 
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
@@ -337,24 +340,28 @@
         return false;
     }
 
-    len = msg.data.size;
+    unsigned int len = msg.data.size;
     if (len > buffer.size()) { // TODO: resize buffer?
         SendSyncFail(s, "oversize data message");
         return false;
     }
     if (!ReadFdExactly(s, &buffer[0], len)) return false;
 
-    ret = symlink(&buffer[0], path.c_str());
-    if (ret && errno == ENOENT) {
-        if (!secure_mkdirs(android::base::Dirname(path))) {
-            SendSyncFailErrno(s, "secure_mkdirs failed");
+    std::string buf_link;
+    if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
+        adb_unlink(path.c_str());
+        auto ret = symlink(&buffer[0], path.c_str());
+        if (ret && errno == ENOENT) {
+            if (!secure_mkdirs(Dirname(path))) {
+                SendSyncFailErrno(s, "secure_mkdirs failed");
+                return false;
+            }
+            ret = symlink(&buffer[0], path.c_str());
+        }
+        if (ret) {
+            SendSyncFailErrno(s, "symlink failed");
             return false;
         }
-        ret = symlink(&buffer[0], path.c_str());
-    }
-    if (ret) {
-        SendSyncFailErrno(s, "symlink failed");
-        return false;
     }
 
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -391,7 +398,8 @@
 
     // Don't delete files before copying if they are not "regular" or symlinks.
     struct stat st;
-    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
+                     (S_ISLNK(st.st_mode) && !S_ISLNK(mode));
     if (do_unlink) {
         adb_unlink(path.c_str());
     }
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index b40faee..fe79acd 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -212,6 +212,7 @@
 
 static void jdwp_process_event(int socket, unsigned events, void* _proc) {
     JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
+    CHECK_EQ(socket, proc->socket);
 
     if (events & FDE_READ) {
         if (proc->pid < 0) {
@@ -225,82 +226,50 @@
             D("Adding pid %d to jdwp process list", proc->pid);
             jdwp_process_list_updated();
         } else {
-            /* the pid was read, if we get there it's probably because the connection
-             * was closed (e.g. the JDWP process exited or crashed) */
-            char buf[32];
-
-            while (true) {
-                int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
-
-                if (len == 0) {
-                    D("terminating JDWP %d connection: EOF", proc->pid);
-                    break;
-                } else if (len < 0) {
-                    if (len < 0 && errno == EAGAIN) {
-                        return;
-                    }
-
-                    D("terminating JDWP %d connection: EOF", proc->pid);
-                    break;
-                } else {
-                    D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
-                      len);
-                }
-            }
-
+            // We already have the PID, if we can read from the socket, we've probably hit EOF.
+            D("terminating JDWP connection %d", proc->pid);
             goto CloseProcess;
         }
     }
 
     if (events & FDE_WRITE) {
         D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
-        if (!proc->out_fds.empty()) {
-            int fd = proc->out_fds.back().get();
-            struct cmsghdr* cmsg;
-            struct msghdr msg;
-            struct iovec iov;
-            char dummy = '!';
-            char buffer[sizeof(struct cmsghdr) + sizeof(int)];
+        CHECK(!proc->out_fds.empty());
 
-            iov.iov_base = &dummy;
-            iov.iov_len = 1;
-            msg.msg_name = nullptr;
-            msg.msg_namelen = 0;
-            msg.msg_iov = &iov;
-            msg.msg_iovlen = 1;
-            msg.msg_flags = 0;
-            msg.msg_control = buffer;
-            msg.msg_controllen = sizeof(buffer);
+        int fd = proc->out_fds.back().get();
+        struct cmsghdr* cmsg;
+        struct msghdr msg;
+        struct iovec iov;
+        char dummy = '!';
+        char buffer[sizeof(struct cmsghdr) + sizeof(int)];
 
-            cmsg = CMSG_FIRSTHDR(&msg);
-            cmsg->cmsg_len = msg.msg_controllen;
-            cmsg->cmsg_level = SOL_SOCKET;
-            cmsg->cmsg_type = SCM_RIGHTS;
-            ((int*)CMSG_DATA(cmsg))[0] = fd;
+        iov.iov_base = &dummy;
+        iov.iov_len = 1;
+        msg.msg_name = nullptr;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+        msg.msg_flags = 0;
+        msg.msg_control = buffer;
+        msg.msg_controllen = sizeof(buffer);
 
-            if (!set_file_block_mode(proc->socket, true)) {
-                VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
-                goto CloseProcess;
-            }
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_len = msg.msg_controllen;
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        ((int*)CMSG_DATA(cmsg))[0] = fd;
 
-            int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
-            if (ret < 0) {
-                D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
-                goto CloseProcess;
-            }
+        int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
+        if (ret < 0) {
+            D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
+            goto CloseProcess;
+        }
 
-            D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
+        D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
 
-            proc->out_fds.pop_back();
-
-            if (!set_file_block_mode(proc->socket, false)) {
-                VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
-                goto CloseProcess;
-            }
-
-            if (proc->out_fds.empty()) {
-                fdevent_del(proc->fde, FDE_WRITE);
-            }
+        proc->out_fds.pop_back();
+        if (proc->out_fds.empty()) {
+            fdevent_del(proc->fde, FDE_WRITE);
         }
     }
 
@@ -406,9 +375,10 @@
     return 0;
 }
 
-static void jdwp_control_event(int s, unsigned events, void* _control) {
+static void jdwp_control_event(int fd, unsigned events, void* _control) {
     JdwpControl* control = (JdwpControl*)_control;
 
+    CHECK_EQ(fd, control->listen_socket);
     if (events & FDE_READ) {
         int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
         if (s < 0) {
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index cf4d294..ae02525 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -35,6 +35,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/properties.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
@@ -47,6 +48,8 @@
 #include "adb_utils.h"
 #include "set_verity_enable_state_service.h"
 
+using android::base::Realpath;
+
 // Returns the last device used to mount a directory in /proc/mounts.
 // This will find overlayfs entry where upperdir=lowerdir, to make sure
 // remount is associated with the correct directory.
@@ -55,9 +58,15 @@
     std::string mnt_fsname;
     if (!fp) return mnt_fsname;
 
+    // dir might be a symlink, e.g., /product -> /system/product in GSI.
+    std::string canonical_path;
+    if (!Realpath(dir, &canonical_path)) {
+        PLOG(ERROR) << "Realpath failed: " << dir;
+    }
+
     mntent* e;
     while ((e = getmntent(fp.get())) != nullptr) {
-        if (strcmp(dir, e->mnt_dir) == 0) {
+        if (canonical_path == e->mnt_dir) {
             mnt_fsname = e->mnt_fsname;
         }
     }
@@ -146,6 +155,10 @@
         return true;
     }
     bool is_root = strcmp(dir, "/") == 0;
+    if (is_root && !find_mount("/system", false).empty()) {
+        dir = "/system";
+        is_root = false;
+    }
     std::string dev = find_mount(dir, is_root);
     // Even if the device for the root is not found, we still try to remount it
     // as rw. This typically only happens when running Android in a container:
@@ -216,13 +229,19 @@
 
     // If we can use overlayfs, lets get it in place first
     // before we struggle with determining deduplication operations.
-    if (!verity_enabled && fs_mgr_overlayfs_setup() && fs_mgr_overlayfs_mount_all()) {
-        WriteFdExactly(fd.get(), "overlayfs mounted\n");
+    if (!verity_enabled && fs_mgr_overlayfs_setup()) {
+        std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                                   fs_mgr_free_fstab);
+        if (fs_mgr_overlayfs_mount_all(fstab.get())) {
+            WriteFdExactly(fd.get(), "overlayfs mounted\n");
+        }
     }
 
     // Find partitions that are deduplicated, and can be un-deduplicated.
     std::set<std::string> dedup;
-    for (const auto& partition : partitions) {
+    for (const auto& part : partitions) {
+        auto partition = part;
+        if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system";
         std::string dev = find_mount(partition.c_str(), partition == "/");
         if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
             continue;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 8417690..720ec6a 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -103,7 +103,9 @@
     if (reboot_arg.empty()) reboot_arg = "adb";
     std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
 
-    if (reboot_arg == "fastboot" && access("/dev/socket/recovery", F_OK) == 0) {
+    if (reboot_arg == "fastboot" &&
+        android::base::GetBoolProperty("ro.boot.logical_partitions", false) &&
+        access("/dev/socket/recovery", F_OK) == 0) {
         LOG(INFO) << "Recovery specific reboot fastboot";
         /*
          * The socket is created to allow switching between recovery and
@@ -200,6 +202,27 @@
     return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
 }
 
+static void spin_service(unique_fd fd) {
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "refusing to spin on non-debuggable build\n");
+        return;
+    }
+
+    // A service that creates an fdevent that's always pending, and then ignores it.
+    unique_fd pipe_read, pipe_write;
+    if (!Pipe(&pipe_read, &pipe_write)) {
+        WriteFdExactly(fd.get(), "failed to create pipe\n");
+        return;
+    }
+
+    fdevent_run_on_main_thread([fd = pipe_read.release()]() {
+        fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
+        fdevent_add(fde, FDE_READ);
+    });
+
+    WriteFdExactly(fd.get(), "spinning\n");
+}
+
 unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
     if (!strncmp("dev:", name, 4)) {
         return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
@@ -252,6 +275,9 @@
     } else if (!strcmp(name, "reconnect")) {
         return create_service_thread(
                 "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
+    } else if (!strcmp(name, "spin")) {
+        return create_service_thread("spin", spin_service);
     }
+
     return unique_fd{};
 }
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index dee87bd..e096560 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -35,6 +35,8 @@
 #include <unordered_map>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
@@ -44,6 +46,7 @@
 #include "adb_trace.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "sysdeps/chrono.h"
 
 #define FDE_EVENTMASK  0x00ff
 #define FDE_STATEMASK  0xff00
@@ -147,24 +150,34 @@
     return fde;
 }
 
-void fdevent_destroy(fdevent* fde) {
+unique_fd fdevent_release(fdevent* fde) {
     check_main_thread();
-    if (fde == nullptr) return;
+    if (!fde) {
+        return {};
+    }
+
     if (!(fde->state & FDE_CREATED)) {
         LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
     }
 
+    unique_fd result = std::move(fde->fd);
     if (fde->state & FDE_ACTIVE) {
-        g_poll_node_map.erase(fde->fd.get());
+        g_poll_node_map.erase(result.get());
+
         if (fde->state & FDE_PENDING) {
             g_pending_list.remove(fde);
         }
-        fde->fd.reset();
         fde->state = 0;
         fde->events = 0;
     }
 
     delete fde;
+    return result;
+}
+
+void fdevent_destroy(fdevent* fde) {
+    // Release, and then let unique_fd's destructor cleanup.
+    fdevent_release(fde);
 }
 
 static void fdevent_update(fdevent* fde, unsigned events) {
@@ -237,6 +250,7 @@
     }
     CHECK_GT(pollfds.size(), 0u);
     D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+
     int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
     if (ret == -1) {
         PLOG(ERROR) << "poll(), ret = " << ret;
@@ -357,10 +371,66 @@
     }
 }
 
+static void fdevent_check_spin(uint64_t cycle) {
+    // Check to see if we're spinning because we forgot about an fdevent
+    // by keeping track of how long fdevents have been continuously pending.
+    struct SpinCheck {
+        fdevent* fde;
+        android::base::boot_clock::time_point timestamp;
+        uint64_t cycle;
+    };
+    static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+    static auto last_cycle = android::base::boot_clock::now();
+
+    auto now = android::base::boot_clock::now();
+    if (now - last_cycle > 10ms) {
+        // We're not spinning.
+        g_continuously_pending.clear();
+        last_cycle = now;
+        return;
+    }
+    last_cycle = now;
+
+    for (auto* fde : g_pending_list) {
+        auto it = g_continuously_pending.find(fde->id);
+        if (it == g_continuously_pending.end()) {
+            g_continuously_pending[fde->id] =
+                    SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
+        } else {
+            it->second.cycle = cycle;
+        }
+    }
+
+    for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
+        if (it->second.cycle != cycle) {
+            it = g_continuously_pending.erase(it);
+        } else {
+            // Use an absurdly long window, since all we really care about is
+            // getting a bugreport eventually.
+            if (now - it->second.timestamp > 300s) {
+                LOG(FATAL_WITHOUT_ABORT)
+                        << "detected spin in fdevent: " << dump_fde(it->second.fde);
+#if defined(__linux__)
+                int fd = it->second.fde->fd.get();
+                std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+                std::string path;
+                if (!android::base::Readlink(fd_path, &path)) {
+                    PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
+                }
+                LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
+#endif
+                abort();
+            }
+            ++it;
+        }
+    }
+}
+
 void fdevent_loop() {
     set_main_thread();
     fdevent_run_setup();
 
+    uint64_t cycle = 0;
     while (true) {
         if (terminate_loop) {
             return;
@@ -370,6 +440,8 @@
 
         fdevent_process();
 
+        fdevent_check_spin(cycle++);
+
         while (!g_pending_list.empty()) {
             fdevent* fde = g_pending_list.front();
             g_pending_list.pop_front();
diff --git a/adb/fdevent.h b/adb/fdevent.h
index d501b86..df2339a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -50,11 +50,12 @@
 */
 fdevent *fdevent_create(int fd, fd_func func, void *arg);
 
-/* Uninitialize and deallocate an fdevent object that was
-** created by fdevent_create()
-*/
+// Deallocate an fdevent object that was created by fdevent_create.
 void fdevent_destroy(fdevent *fde);
 
+// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
+unique_fd fdevent_release(fdevent* fde);
+
 /* Change which events should cause notifications
 */
 void fdevent_set(fdevent *fde, unsigned events);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index be0bdd0..b8d7e06 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -52,10 +52,11 @@
 #include <fcntl.h>
 #include <io.h>
 #include <process.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <utime.h>
-#include <winsock2.h>
 #include <windows.h>
+#include <winsock2.h>
 #include <ws2tcpip.h>
 
 #include <memory>   // unique_ptr
@@ -92,7 +93,7 @@
 extern int adb_creat(const char* path, int mode);
 extern int adb_read(int fd, void* buf, int len);
 extern int adb_write(int fd, const void* buf, int len);
-extern int adb_lseek(int fd, int pos, int where);
+extern int64_t adb_lseek(int fd, int64_t pos, int where);
 extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
 extern int adb_close(int fd);
 extern int adb_register_socket(SOCKET s);
@@ -315,25 +316,24 @@
 
 #else /* !_WIN32 a.k.a. Unix */
 
-#include <cutils/sockets.h>
 #include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include <string>
 
+#include <cutils/sockets.h>
+
 #define OS_PATH_SEPARATORS "/"
 #define OS_PATH_SEPARATOR '/'
 #define OS_PATH_SEPARATOR_STR "/"
@@ -443,12 +443,15 @@
 #undef   write
 #define  write  ___xxx_write
 
-static __inline__ int   adb_lseek(int  fd, int  pos, int  where)
-{
+static __inline__ int64_t adb_lseek(int fd, int64_t pos, int where) {
+#if defined(__APPLE__)
     return lseek(fd, pos, where);
+#else
+    return lseek64(fd, pos, where);
+#endif
 }
-#undef   lseek
-#define  lseek   ___xxx_lseek
+#undef lseek
+#define lseek ___xxx_lseek
 
 static __inline__  int    adb_unlink(const char*  path)
 {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 026dd1c..8784757 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -52,12 +52,11 @@
 
 typedef const struct FHClassRec_* FHClass;
 typedef struct FHRec_* FH;
-typedef struct EventHookRec_* EventHook;
 
 typedef struct FHClassRec_ {
     void (*_fh_init)(FH);
     int (*_fh_close)(FH);
-    int (*_fh_lseek)(FH, int, int);
+    int64_t (*_fh_lseek)(FH, int64_t, int);
     int (*_fh_read)(FH, void*, int);
     int (*_fh_write)(FH, const void*, int);
     int (*_fh_writev)(FH, const adb_iovec*, int);
@@ -65,7 +64,7 @@
 
 static void _fh_file_init(FH);
 static int _fh_file_close(FH);
-static int _fh_file_lseek(FH, int, int);
+static int64_t _fh_file_lseek(FH, int64_t, int);
 static int _fh_file_read(FH, void*, int);
 static int _fh_file_write(FH, const void*, int);
 static int _fh_file_writev(FH, const adb_iovec*, int);
@@ -81,7 +80,7 @@
 
 static void _fh_socket_init(FH);
 static int _fh_socket_close(FH);
-static int _fh_socket_lseek(FH, int, int);
+static int64_t _fh_socket_lseek(FH, int64_t, int);
 static int _fh_socket_read(FH, void*, int);
 static int _fh_socket_write(FH, const void*, int);
 static int _fh_socket_writev(FH, const adb_iovec*, int);
@@ -318,10 +317,8 @@
     return wrote_bytes;
 }
 
-static int _fh_file_lseek(FH f, int pos, int origin) {
+static int64_t _fh_file_lseek(FH f, int64_t pos, int origin) {
     DWORD method;
-    DWORD result;
-
     switch (origin) {
         case SEEK_SET:
             method = FILE_BEGIN;
@@ -337,14 +334,13 @@
             return -1;
     }
 
-    result = SetFilePointer(f->fh_handle, pos, nullptr, method);
-    if (result == INVALID_SET_FILE_POINTER) {
+    LARGE_INTEGER li = {.QuadPart = pos};
+    if (!SetFilePointerEx(f->fh_handle, li, &li, method)) {
         errno = EIO;
         return -1;
-    } else {
-        f->eof = 0;
     }
-    return (int)result;
+    f->eof = 0;
+    return li.QuadPart;
 }
 
 /**************************************************************************/
@@ -491,14 +487,12 @@
     return f->clazz->_fh_writev(f, iov, iovcnt);
 }
 
-int adb_lseek(int fd, int pos, int where) {
+int64_t adb_lseek(int fd, int64_t pos, int where) {
     FH f = _fh_from_int(fd, __func__);
-
     if (!f) {
         errno = EBADF;
         return -1;
     }
-
     return f->clazz->_fh_lseek(f, pos, where);
 }
 
@@ -644,7 +638,7 @@
     return 0;
 }
 
-static int _fh_socket_lseek(FH f, int pos, int origin) {
+static int64_t _fh_socket_lseek(FH f, int64_t pos, int origin) {
     errno = EPIPE;
     return -1;
 }
diff --git a/adb/test_device.py b/adb/test_device.py
old mode 100644
new mode 100755
index 9f45115..c3166ff
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -751,7 +751,7 @@
                 shutil.rmtree(host_dir)
 
     def test_push_empty(self):
-        """Push a directory containing an empty directory to the device."""
+        """Push an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
 
@@ -767,9 +767,10 @@
 
             self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
 
-            test_empty_cmd = ['[', '-d',
-                              os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
+            remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
+            test_empty_cmd = ["[", "-d", remote_path, "]"]
             rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+
             self.assertEqual(rc, 0)
             self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         finally:
diff --git a/adb/types.h b/adb/types.h
index a3e5d48..1f7008e 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -42,14 +42,14 @@
     }
 
     Block(const Block& copy) = delete;
-    Block(Block&& move) {
+    Block(Block&& move) noexcept {
         std::swap(data_, move.data_);
         std::swap(capacity_, move.capacity_);
         std::swap(size_, move.size_);
     }
 
     Block& operator=(const Block& copy) = delete;
-    Block& operator=(Block&& move) {
+    Block& operator=(Block&& move) noexcept {
         clear();
 
         std::swap(data_, move.data_);
@@ -147,12 +147,10 @@
     }
 
     IOVector(const IOVector& copy) = delete;
-    IOVector(IOVector&& move) : IOVector() {
-        *this = std::move(move);
-    }
+    IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
 
     IOVector& operator=(const IOVector& copy) = delete;
-    IOVector& operator=(IOVector&& move) {
+    IOVector& operator=(IOVector&& move) noexcept {
         chain_ = std::move(move.chain_);
         chain_length_ = move.chain_length_;
         begin_offset_ = move.begin_offset_;
diff --git a/base/Android.bp b/base/Android.bp
index 3d80d97..daa820a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -56,6 +56,7 @@
         "test_utils.cpp",
     ],
 
+    cppflags: ["-Wexit-time-destructors"],
     shared_libs: ["liblog"],
     target: {
         android: {
@@ -68,13 +69,11 @@
             srcs: [
                 "errors_unix.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
         },
         darwin: {
             srcs: [
                 "errors_unix.cpp",
             ],
-            cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
             enabled: true,
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index e6a9d10..5a224d6 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -28,7 +28,7 @@
  public:
   ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
 
-  ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+  ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
     that.active_ = false;
   }
 
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 71025ad..cd2dc04 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -90,8 +90,8 @@
   explicit unique_fd_impl(int fd) { reset(fd); }
   ~unique_fd_impl() { reset(); }
 
-  unique_fd_impl(unique_fd_impl&& other) { reset(other.release()); }
-  unique_fd_impl& operator=(unique_fd_impl&& s) {
+  unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
+  unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
     int fd = s.fd_;
     s.fd_ = -1;
     reset(fd, &s);
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c17e00f..6700b6c 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -236,7 +236,7 @@
     {"reboot,rescueparty", 90},
     {"charge", 91},
     {"oem_tz_crash", 92},
-    {"uvlo", 93},
+    {"uvlo", 93},  // aliasReasons converts to reboot,undervoltage
     {"oem_ps_hold", 94},
     {"abnormal_reset", 95},
     {"oemerr_unknown", 96},
@@ -248,9 +248,9 @@
     {"watchdog_nonsec", 102},
     {"watchdog_apps_bark", 103},
     {"reboot_dmverity_corrupted", 104},
-    {"reboot_smpl", 105},
+    {"reboot_smpl", 105},  // aliasReasons converts to reboot,powerloss
     {"watchdog_sdi_apps_reset", 106},
-    {"smpl", 107},
+    {"smpl", 107},  // aliasReasons converts to reboot,powerloss
     {"oem_modem_failed_to_powerup", 108},
     {"reboot_normal", 109},
     {"oem_lpass_cfg", 110},
@@ -262,8 +262,8 @@
     {"oem_rpm_undef_error", 116},
     {"oem_crash_on_the_lk", 117},
     {"oem_rpm_reset", 118},
-    {"REUSE1", 119},  // Former dupe, can be re-used
-    {"REUSE2", 120},  // Former dupe, can be re-used
+    {"reboot,powerloss", 119},
+    {"reboot,undervoltage", 120},
     {"factory_cable", 121},
     {"oem_ar6320_failed_to_powerup", 122},
     {"watchdog_rpm_bite", 123},
@@ -840,6 +840,8 @@
         {"reboot,tool", "tool_by_pass_pwk"},
         {"!reboot,longkey", "reboot_longkey"},
         {"!reboot,longkey", "kpdpwr"},
+        {"!reboot,undervoltage", "uvlo"},
+        {"!reboot,powerloss", "smpl"},
         {"bootloader", ""},
     };
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 93f7572..577e336 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -34,6 +34,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -282,6 +283,7 @@
   switch (crash_info->header.version) {
     case 2:
       *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+      FALLTHROUGH_INTENDED;
     case 1:
       *abort_msg_address = crash_info->data.v1.abort_msg_address;
       *siginfo = crash_info->data.v1.siginfo;
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0fe1d0..f0bdfbf 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -224,7 +224,7 @@
     // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
       char buf[1];
-      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+      UNUSED(TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf))));
       return do_action(arg + strlen("wait-"));
     } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
       errno = 0;
@@ -258,10 +258,14 @@
       __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
       __assert2("some_file.c", 123, "some_function", "false");
+#if !defined(__clang_analyzer__)
     } else if (!strcasecmp(arg, "fortify")) {
+      // FORTIFY is disabled when running clang-tidy and other tools, so this
+      // shouldn't depend on internal implementation details of it.
       char buf[10];
       __read_chk(-1, buf, 32, 10);
       while (true) pause();
+#endif
     } else if (!strcasecmp(arg, "fdsan_file")) {
       FILE* f = fopen("/dev/null", "r");
       close(fileno(f));
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 6b175af..50d18ed 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -122,6 +122,7 @@
     shared_libs: [
         "android.hardware.boot@1.0",
         "android.hardware.fastboot@1.0",
+        "android.hardware.health@2.0",
         "libadbd",
         "libasyncio",
         "libbase",
@@ -139,6 +140,10 @@
         "libutils",
     ],
 
+    static_libs: [
+        "libhealthhalutils",
+    ],
+
     cpp_std: "c++17",
 }
 
@@ -202,7 +207,6 @@
     cpp_std: "c++17",
     srcs: [
         "bootimg_utils.cpp",
-        "engine.cpp",
         "fastboot.cpp",
         "fs.cpp",
         "socket.cpp",
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 1152007..e433787 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -39,27 +39,27 @@
     strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size) {
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out) {
     const size_t page_mask = src.page_size - 1;
 
     int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
-    int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
-    int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
-    int64_t second_actual = (second_size + page_mask) & (~page_mask);
+    int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
+    int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
+    int64_t second_actual = (second.size() + page_mask) & (~page_mask);
 
-    *bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    int64_t bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    out->resize(bootimg_size);
 
-    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
-    if (hdr == nullptr) die("couldn't allocate boot image: %" PRId64 " bytes", *bootimg_size);
+    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(out->data());
 
     *hdr = src;
     memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
 
-    hdr->kernel_size =  kernel_size;
-    hdr->ramdisk_size = ramdisk_size;
-    hdr->second_size =  second_size;
+    hdr->kernel_size = kernel.size();
+    hdr->ramdisk_size = ramdisk.size();
+    hdr->second_size = second.size();
 
     hdr->kernel_addr += base;
     hdr->ramdisk_addr += base;
@@ -70,8 +70,9 @@
         hdr->header_size = sizeof(boot_img_hdr_v1);
     }
 
-    memcpy(hdr->magic + hdr->page_size, kernel, kernel_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk, ramdisk_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second, second_size);
+    memcpy(hdr->magic + hdr->page_size, kernel.data(), kernel.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),
+           second.size());
     return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index fe805b0..a4e8870 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -33,8 +33,9 @@
 #include <sys/types.h>
 
 #include <string>
+#include <vector>
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size);
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out);
 void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 57e25fc..705da33 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -32,6 +32,7 @@
 #define FB_CMD_DELETE_PARTITION "delete-logical-partition"
 #define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
 #define FB_CMD_UPDATE_SUPER "update-super"
+#define FB_CMD_OEM "oem"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
@@ -59,3 +60,7 @@
 #define FB_VAR_IS_LOGICAL "is-logical"
 #define FB_VAR_IS_USERSPACE "is-userspace"
 #define FB_VAR_HW_REVISION "hw-revision"
+#define FB_VAR_VARIANT "variant"
+#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
+#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
+#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 0ec0994..b3fce54 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -27,6 +27,7 @@
 #include <android-base/unique_fd.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <uuid/uuid.h>
@@ -40,6 +41,9 @@
 using ::android::hardware::boot::V1_0::BoolResult;
 using ::android::hardware::boot::V1_0::CommandResult;
 using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
+
 using namespace android::fs_mgr;
 
 struct VariableHandlers {
@@ -79,6 +83,7 @@
             {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
             {FB_VAR_PRODUCT, {GetProduct, nullptr}},
             {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+            {FB_VAR_VARIANT, {GetVariant, nullptr}},
             {FB_VAR_SECURE, {GetSecure, nullptr}},
             {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
             {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
@@ -91,6 +96,9 @@
             {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
             {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
             {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+            {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
+            {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+            {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
             {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
 
     if (args.size() < 2) {
@@ -123,6 +131,11 @@
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
     }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
+    }
+
     PartitionHandle handle;
     if (!OpenPartition(device, args[1], &handle)) {
         return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
@@ -133,10 +146,34 @@
     return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
 }
 
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
+    if (!ret_val.isOk()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to do OEM command");
+    }
+    if (ret.status != Status::SUCCESS) {
+        return device->WriteStatus(FastbootResult::FAIL, ret.message);
+    }
+
+    return device->WriteStatus(FastbootResult::OKAY, ret.message);
+}
+
 bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
     }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Download is not allowed on locked devices");
+    }
+
     // arg[0] is the command name, arg[1] contains size of data to be downloaded
     unsigned int size;
     if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) {
@@ -159,6 +196,12 @@
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
     }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Flashing is not allowed on locked devices");
+    }
+
     int ret = Flash(device, args[1]);
     if (ret < 0) {
         return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
@@ -171,6 +214,11 @@
         return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
     }
 
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "set_active command is not allowed on locked devices");
+    }
+
     // Slot suffix needs to be between 'a' and 'z'.
     Slot slot;
     if (!GetSlotNumber(args[1], &slot)) {
@@ -280,7 +328,7 @@
 };
 
 PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
-    auto super_device = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name());
     if (!super_device) {
         return;
     }
@@ -304,6 +352,10 @@
         return device->WriteFail("Invalid partition name and size");
     }
 
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
     uint64_t partition_size;
     std::string partition_name = args[1];
     if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
@@ -319,13 +371,7 @@
         return device->WriteFail("Partition already exists");
     }
 
-    // Make a random UUID, since they're not currently used.
-    uuid_t uuid;
-    char uuid_str[37];
-    uuid_generate_random(uuid);
-    uuid_unparse(uuid, uuid_str);
-
-    Partition* partition = builder->AddPartition(partition_name, uuid_str, 0);
+    Partition* partition = builder->AddPartition(partition_name, 0);
     if (!partition) {
         return device->WriteFail("Failed to add partition");
     }
@@ -344,6 +390,10 @@
         return device->WriteFail("Invalid partition name and size");
     }
 
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
     PartitionBuilder builder(device);
     if (!builder.Valid()) {
         return device->WriteFail("Could not open super partition");
@@ -360,6 +410,10 @@
         return device->WriteFail("Invalid partition name and size");
     }
 
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
     uint64_t partition_size;
     std::string partition_name = args[1];
     if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
@@ -388,6 +442,11 @@
     if (args.size() < 2) {
         return device->WriteFail("Invalid arguments");
     }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+    }
+
     bool wipe = (args.size() >= 3 && args[2] == "wipe");
     return UpdateSuper(device, args[1], wipe);
 }
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 4778d23..9df43a9 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -45,3 +45,4 @@
 bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index ae2e7a6..b843c05 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -20,6 +20,8 @@
 #include <android-base/strings.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <healthhalutils/HealthHalUtils.h>
+
 #include <algorithm>
 
 #include "constants.h"
@@ -30,6 +32,8 @@
 using ::android::hardware::boot::V1_0::IBootControl;
 using ::android::hardware::boot::V1_0::Slot;
 using ::android::hardware::fastboot::V1_0::IFastboot;
+using ::android::hardware::health::V2_0::get_health_service;
+
 namespace sph = std::placeholders;
 
 FastbootDevice::FastbootDevice()
@@ -48,9 +52,11 @@
               {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
               {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
               {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
+              {FB_CMD_OEM, OemCmdHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
+      health_hal_(get_health_service()),
       fastboot_hal_(IFastboot::getService()) {}
 
 FastbootDevice::~FastbootDevice() {
@@ -120,10 +126,20 @@
         command[bytes_read] = '\0';
 
         LOG(INFO) << "Fastboot command: " << command;
-        auto args = android::base::Split(command, ":");
-        auto found_command = kCommandMap.find(args[0]);
+
+        std::vector<std::string> args;
+        std::string cmd_name;
+        if (android::base::StartsWith(command, "oem ")) {
+            args = {command};
+            cmd_name = "oem";
+        } else {
+            args = android::base::Split(command, ":");
+            cmd_name = args[0];
+        }
+
+        auto found_command = kCommandMap.find(cmd_name);
         if (found_command == kCommandMap.end()) {
-            WriteStatus(FastbootResult::FAIL, "Unrecognized command");
+            WriteStatus(FastbootResult::FAIL, "Unrecognized command " + args[0]);
             continue;
         }
         if (!found_command->second(this, args)) {
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 189cf80..2eb7177 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -24,6 +24,7 @@
 
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <android/hardware/health/2.0/IHealth.h>
 
 #include "commands.h"
 #include "transport.h"
@@ -53,12 +54,14 @@
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
         return fastboot_hal_;
     }
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
 
   private:
     const std::unordered_map<std::string, CommandHandler> kCommandMap;
 
     std::unique_ptr<Transport> transport_;
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
 };
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index a383c54..4fc3d1d 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -146,8 +146,7 @@
         if (builder->FindPartition(name)) {
             continue;
         }
-        std::string guid = GetPartitionGuid(partition);
-        if (!builder->AddPartition(name, guid, partition.attributes)) {
+        if (!builder->AddPartition(name, partition.attributes)) {
             return device->WriteFail("Unable to add partition: " + name);
         }
     }
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 261a202..528abec 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -21,7 +21,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <liblp/liblp.h>
 
@@ -43,7 +45,7 @@
 
 static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
                                  PartitionHandle* handle) {
-    std::optional<std::string> path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    std::optional<std::string> path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
     if (!path) {
         return false;
     }
@@ -99,7 +101,7 @@
 
 bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
                             bool* is_zero_length) {
-    auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
     if (!path) {
         return false;
     }
@@ -148,7 +150,7 @@
     }
 
     // Next get logical partitions.
-    if (auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME)) {
+    if (auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name())) {
         uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
         if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
             for (const auto& partition : metadata->partitions) {
@@ -159,3 +161,9 @@
     }
     return partitions;
 }
+
+bool GetDeviceLockStatus() {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 4f0d079..bb08f72 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -58,3 +58,4 @@
 bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
 bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
 std::vector<std::string> ListPartitions(FastbootDevice* device);
+bool GetDeviceLockStatus();
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 7535248..2de79b1 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -24,6 +24,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <ext4_utils/ext4_utils.h>
+#include <healthhalutils/HealthHalUtils.h>
 
 #include "fastboot_device.h"
 #include "flashing.h"
@@ -74,6 +75,110 @@
     return true;
 }
 
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                std::string* message) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->getVariant([&](std::string device_variant, Result result) {
+        *message = device_variant;
+        ret = result;
+    });
+    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+        *message = "Unable to get device variant";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatteryVoltageHelper(FastbootDevice* device, int32_t* battery_voltage) {
+    using android::hardware::health::V2_0::HealthInfo;
+    using android::hardware::health::V2_0::Result;
+
+    auto health_hal = device->health_hal();
+    if (!health_hal) {
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
+        *battery_voltage = info.legacy.batteryVoltage;
+        ret = result;
+    });
+    if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                     std::string* message) {
+    int32_t battery_voltage = 0;
+    if (!GetBatteryVoltageHelper(device, &battery_voltage)) {
+        *message = "Unable to read battery voltage";
+        return false;
+    }
+
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->getBatteryVoltageFlashingThreshold(
+            [&](int32_t voltage_threshold, Result result) {
+                *message = battery_voltage >= voltage_threshold ? "yes" : "no";
+                ret = result;
+            });
+
+    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+        *message = "Unable to get battery voltage flashing threshold";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                           std::string* message) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val =
+            fastboot_hal->getOffModeChargeState([&](bool off_mode_charging_state, Result result) {
+                *message = off_mode_charging_state ? "1" : "0";
+                ret = result;
+            });
+    if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+        *message = "Unable to get off mode charge state";
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                       std::string* message) {
+    int32_t battery_voltage = 0;
+    if (GetBatteryVoltageHelper(device, &battery_voltage)) {
+        *message = std::to_string(battery_voltage);
+        return true;
+    }
+    *message = "Unable to get battery voltage";
+    return false;
+}
+
 bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
                     std::string* message) {
     std::string suffix = device->GetCurrentSlot();
@@ -148,7 +253,7 @@
 
 bool GetUnlocked(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
                  std::string* message) {
-    *message = "yes";
+    *message = GetDeviceLockStatus() ? "no" : "yes";
     return true;
 }
 
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 63f2670..59b71e8 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -52,6 +52,13 @@
                     std::string* message);
 bool GetHardwareRevision(FastbootDevice* device, const std::vector<std::string>& args,
                          std::string* message);
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message);
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
+                     std::string* message);
 
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
deleted file mode 100644
index 0ac57af..0000000
--- a/fastboot/engine.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include "engine.h"
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-
-#include "constants.h"
-#include "transport.h"
-
-using android::base::StringPrintf;
-
-static fastboot::FastBootDriver* fb = nullptr;
-
-void fb_init(fastboot::FastBootDriver& fbi) {
-    fb = &fbi;
-    auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
-    fb->SetInfoCallback(cb);
-}
-
-void fb_reinit(Transport* transport) {
-    if (Transport* old_transport = fb->set_transport(transport)) {
-        delete old_transport;
-    }
-}
-
-const std::string fb_get_error() {
-    return fb->Error();
-}
-
-bool fb_getvar(const std::string& key, std::string* value) {
-    return !fb->GetVar(key, value);
-}
-
-static void HandleResult(double start, int status) {
-    if (status) {
-        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
-        die("Command failed");
-    } else {
-        double split = now();
-        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
-    }
-}
-
-#define RUN_COMMAND(command)         \
-    {                                \
-        double start = now();        \
-        auto status = (command);     \
-        HandleResult(start, status); \
-    }
-
-void fb_set_active(const std::string& slot) {
-    Status("Setting current slot to '" + slot + "'");
-    RUN_COMMAND(fb->SetActive(slot));
-}
-
-void fb_erase(const std::string& partition) {
-    Status("Erasing '" + partition + "'");
-    RUN_COMMAND(fb->Erase(partition));
-}
-
-void fb_flash_fd(const std::string& partition, int fd, uint32_t sz) {
-    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
-    RUN_COMMAND(fb->Download(fd, sz));
-
-    Status("Writing '" + partition + "'");
-    RUN_COMMAND(fb->Flash(partition));
-}
-
-void fb_flash(const std::string& partition, void* data, uint32_t sz) {
-    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
-    RUN_COMMAND(fb->Download(static_cast<char*>(data), sz));
-
-    Status("Writing '" + partition + "'");
-    RUN_COMMAND(fb->Flash(partition));
-}
-
-void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
-                     size_t current, size_t total) {
-    Status(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
-                        sz / 1024));
-    RUN_COMMAND(fb->Download(s));
-
-    Status(StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current, total));
-    RUN_COMMAND(fb->Flash(partition));
-}
-
-void fb_create_partition(const std::string& partition, const std::string& size) {
-    Status("Creating '" + partition + "'");
-    RUN_COMMAND(fb->RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size));
-}
-
-void fb_delete_partition(const std::string& partition) {
-    Status("Deleting '" + partition + "'");
-    RUN_COMMAND(fb->RawCommand(FB_CMD_DELETE_PARTITION ":" + partition));
-}
-
-void fb_resize_partition(const std::string& partition, const std::string& size) {
-    Status("Resizing '" + partition + "'");
-    RUN_COMMAND(fb->RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size));
-}
-
-static int match(const char* str, const char** value, unsigned count) {
-    unsigned n;
-
-    for (n = 0; n < count; n++) {
-        const char *val = value[n];
-        int len = strlen(val);
-        int match;
-
-        if ((len > 1) && (val[len-1] == '*')) {
-            len--;
-            match = !strncmp(val, str, len);
-        } else {
-            match = !strcmp(val, str);
-        }
-
-        if (match) return 1;
-    }
-
-    return 0;
-}
-
-void fb_require(const std::string& product, const std::string& var, bool invert, size_t count,
-                const char** values) {
-    Status("Checking '" + var + "'");
-
-    double start = now();
-
-    std::string var_value;
-    auto status = fb->GetVar(var, &var_value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        die("requirements not met!");
-    }
-
-    if (!product.empty()) {
-        if (product != cur_product) {
-            double split = now();
-            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
-                    product.c_str(), (split - start));
-            return;
-        }
-    }
-
-    int yes = match(var_value.c_str(), values, count);
-    if (invert) yes = !yes;
-
-    if (yes) {
-        double split = now();
-        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
-        return;
-    }
-
-    fprintf(stderr, "FAILED\n\n");
-    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
-    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", values[0]);
-    for (size_t n = 1; n < count; n++) {
-        fprintf(stderr, " or '%s'", values[n]);
-    }
-    fprintf(stderr, ".\n\n");
-    die("requirements not met!");
-}
-
-void fb_display(const std::string& label, const std::string& var) {
-    std::string value;
-    auto status = fb->GetVar(var, &value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        return;
-    }
-    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
-}
-
-void fb_query_save(const std::string& var, char* dest, uint32_t dest_size) {
-    std::string value;
-    auto status = fb->GetVar(var, &value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        return;
-    }
-
-    strncpy(dest, value.c_str(), dest_size);
-}
-
-void fb_reboot() {
-    fprintf(stderr, "Rebooting");
-    fb->Reboot();
-    fprintf(stderr, "\n");
-}
-
-void fb_command(const std::string& cmd, const std::string& msg) {
-    Status(msg);
-    RUN_COMMAND(fb->RawCommand(cmd));
-}
-
-void fb_download(const std::string& name, void* data, uint32_t size) {
-    Status("Downloading '" + name + "'");
-    RUN_COMMAND(fb->Download(static_cast<char*>(data), size));
-}
-
-void fb_download_fd(const std::string& name, int fd, uint32_t sz) {
-    Status(StringPrintf("Sending '%s' (%u KB)", name.c_str(), sz / 1024));
-    RUN_COMMAND(fb->Download(fd, sz));
-}
-
-void fb_upload(const std::string& outfile) {
-    Status("Uploading '" + outfile + "'");
-    RUN_COMMAND(fb->Upload(outfile));
-}
-
-void fb_notice(const std::string& notice) {
-    Status(notice);
-    fprintf(stderr, "\n");
-}
-
-void fb_wait_for_disconnect() {
-    fb->WaitForDisconnect();
-}
-
-bool fb_reboot_to_userspace() {
-    Status("Rebooting to userspace fastboot");
-    verbose("\n");
-
-    if (fb->RebootTo("fastboot") != fastboot::RetCode::SUCCESS) {
-        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
-        return false;
-    }
-    fprintf(stderr, "OKAY\n");
-
-    fb_reinit(nullptr);
-    return true;
-}
diff --git a/fastboot/engine.h b/fastboot/engine.h
deleted file mode 100644
index d78cb13..0000000
--- a/fastboot/engine.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#pragma once
-
-#include <inttypes.h>
-#include <stdlib.h>
-
-#include <string>
-
-#include <bootimg.h>
-#include "fastboot_driver.h"
-#include "util.h"
-
-#include "constants.h"
-
-class Transport;
-struct sparse_file;
-
-const std::string fb_get_error();
-
-void fb_init(fastboot::FastBootDriver& fbi);
-void fb_reinit(Transport* transport);
-
-bool fb_getvar(const std::string& key, std::string* value);
-void fb_flash(const std::string& partition, void* data, uint32_t sz);
-void fb_flash_fd(const std::string& partition, int fd, uint32_t sz);
-void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
-                     size_t current, size_t total);
-void fb_erase(const std::string& partition);
-void fb_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
-                const char** values);
-void fb_display(const std::string& label, const std::string& var);
-void fb_query_save(const std::string& var, char* dest, uint32_t dest_size);
-void fb_reboot();
-void fb_command(const std::string& cmd, const std::string& msg);
-void fb_download(const std::string& name, void* data, uint32_t size);
-void fb_download_fd(const std::string& name, int fd, uint32_t sz);
-void fb_upload(const std::string& outfile);
-void fb_notice(const std::string& notice);
-void fb_wait_for_disconnect(void);
-void fb_create_partition(const std::string& partition, const std::string& size);
-void fb_delete_partition(const std::string& partition);
-void fb_resize_partition(const std::string& partition, const std::string& size);
-void fb_set_active(const std::string& slot);
-bool fb_reboot_to_userspace();
-
-/* Current product */
-extern char cur_product[FB_RESPONSE_SZ + 1];
-
-class FastBootTool {
-  public:
-    int Main(int argc, char* argv[]);
-
-    void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
-    void ParseOsVersion(boot_img_hdr_v1*, const char*);
-};
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 817afd0..5962650 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -43,6 +45,7 @@
 
 #include <chrono>
 #include <functional>
+#include <regex>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -62,21 +65,23 @@
 
 #include "bootimg_utils.h"
 #include "diagnose_usb.h"
-#include "engine.h"
+#include "fastboot_driver.h"
 #include "fs.h"
 #include "tcp.h"
 #include "transport.h"
 #include "udp.h"
 #include "usb.h"
+#include "util.h"
 
+using android::base::ReadFully;
+using android::base::Split;
+using android::base::Trim;
 using android::base::unique_fd;
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-char cur_product[FB_RESPONSE_SZ + 1];
-
 static const char* serial = nullptr;
 
 static bool g_long_listing = false;
@@ -96,6 +101,8 @@
 
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
+fastboot::FastBootDriver* fb = nullptr;
+
 enum fb_buffer_type {
     FB_BUFFER_FD,
     FB_BUFFER_SPARSE,
@@ -148,6 +155,11 @@
     { nullptr,    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
     { "userdata", "userdata.img",     "userdata.sig", "userdata", true,  ImageType::Extra },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  ImageType::BootCritical },
+    { "vbmeta_mainline",
+                  "vbmeta_mainline.img",
+                                      "vbmeta_mainline.sig",
+                                                      "vbmeta_mainline",
+                                                                  true,  ImageType::BootCritical },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
@@ -172,40 +184,46 @@
     return "";
 }
 
+double last_start_time;
+
+static void Status(const std::string& message) {
+    static constexpr char kStatusFormat[] = "%-50s ";
+    fprintf(stderr, kStatusFormat, message.c_str());
+    last_start_time = now();
+}
+
+static void Epilog(int status) {
+    if (status) {
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+        die("Command failed");
+    } else {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - last_start_time));
+    }
+}
+
+static void InfoMessage(const std::string& info) {
+    fprintf(stderr, "(bootloader) %s\n", info.c_str());
+}
+
 static int64_t get_file_size(int fd) {
     struct stat sb;
-    return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
+    if (fstat(fd, &sb) == -1) {
+        die("could not get file size");
+    }
+    return sb.st_size;
 }
 
-static void* load_fd(int fd, int64_t* sz) {
-    int errno_tmp;
-    char* data = nullptr;
+bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
+    out->clear();
 
-    *sz = get_file_size(fd);
-    if (*sz < 0) {
-        goto oops;
+    unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));
+    if (fd == -1) {
+        return false;
     }
 
-    data = (char*) malloc(*sz);
-    if (data == nullptr) goto oops;
-
-    if(read(fd, data, *sz) != *sz) goto oops;
-    close(fd);
-
-    return data;
-
-oops:
-    errno_tmp = errno;
-    close(fd);
-    if(data != 0) free(data);
-    errno = errno_tmp;
-    return 0;
-}
-
-static void* load_file(const std::string& path, int64_t* sz) {
-    int fd = open(path.c_str(), O_RDONLY | O_BINARY);
-    if (fd == -1) return nullptr;
-    return load_fd(fd, sz);
+    out->resize(get_file_size(fd));
+    return ReadFully(fd, out->data(), out->size());
 }
 
 static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
@@ -413,70 +431,71 @@
     return 0;
 }
 
-static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
-                                 const std::string& second_stage, int64_t* sz) {
-    int64_t ksize;
-    void* kdata = load_file(kernel.c_str(), &ksize);
-    if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+static std::vector<char> LoadBootableImage(const std::string& kernel, const std::string& ramdisk,
+                                           const std::string& second_stage) {
+    std::vector<char> kernel_data;
+    if (!ReadFileToVector(kernel, &kernel_data)) {
+        die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+    }
 
     // Is this actually a boot image?
-    if (ksize < static_cast<int64_t>(sizeof(boot_img_hdr_v1))) {
+    if (kernel_data.size() < sizeof(boot_img_hdr_v1)) {
         die("cannot load '%s': too short", kernel.c_str());
     }
-    if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+    if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
         if (!g_cmdline.empty()) {
-            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), g_cmdline);
+            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kernel_data.data()), g_cmdline);
         }
 
         if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
 
-        *sz = ksize;
-        return kdata;
+        return kernel_data;
     }
 
-    void* rdata = nullptr;
-    int64_t rsize = 0;
+    std::vector<char> ramdisk_data;
     if (!ramdisk.empty()) {
-        rdata = load_file(ramdisk.c_str(), &rsize);
-        if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        if (!ReadFileToVector(ramdisk, &ramdisk_data)) {
+            die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        }
     }
 
-    void* sdata = nullptr;
-    int64_t ssize = 0;
+    std::vector<char> second_stage_data;
     if (!second_stage.empty()) {
-        sdata = load_file(second_stage.c_str(), &ssize);
-        if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        if (!ReadFileToVector(second_stage, &second_stage_data)) {
+            die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        }
     }
-
     fprintf(stderr,"creating boot image...\n");
-    boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, rdata, rsize, sdata, ssize,
-                                       g_base_addr, g_boot_img_hdr, sz);
-    if (bdata == nullptr) die("failed to create boot.img");
 
-    if (!g_cmdline.empty()) bootimg_set_cmdline(bdata, g_cmdline);
-    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", *sz);
+    std::vector<char> out;
+    boot_img_hdr_v1* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+                                                 g_base_addr, g_boot_img_hdr, &out);
 
-    return bdata;
+    if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
+    fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
+
+    return out;
 }
 
-static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
-    ZipString zip_entry_name(entry_name);
+static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
+                          std::vector<char>* out) {
+    ZipString zip_entry_name(entry_name.c_str());
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
-        fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        return nullptr;
+        fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
+        return false;
     }
 
-    *sz = zip_entry.uncompressed_length;
+    out->resize(zip_entry.uncompressed_length);
 
-    fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
-    uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
+    fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
+            out->size() / 1024 / 1024);
 
-    int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
-    if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
+    int error = ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()),
+                                out->size());
+    if (error != 0) die("failed to extract '%s': %s", entry_name.c_str(), ErrorCodeString(error));
 
-    return data;
+    return true;
 }
 
 #if defined(_WIN32)
@@ -597,119 +616,168 @@
     return fd.release();
 }
 
-static char* strip(char* s) {
-    while (*s && isspace(*s)) s++;
+static void CheckRequirement(const std::string& cur_product, const std::string& var,
+                             const std::string& product, bool invert,
+                             const std::vector<std::string>& options) {
+    Status("Checking '" + var + "'");
 
-    int n = strlen(s);
-    while (n-- > 0) {
-        if (!isspace(s[n])) break;
-        s[n] = 0;
+    double start = now();
+
+    if (!product.empty()) {
+        if (product != cur_product) {
+            double split = now();
+            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
+                    cur_product.c_str(), product.c_str(), (split - start));
+            return;
+        }
     }
-    return s;
+
+    std::string var_value;
+    if (fb->GetVar(var, &var_value) != fastboot::SUCCESS) {
+        fprintf(stderr, "FAILED\n\n");
+        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
+                fb->Error().c_str());
+        die("requirements not met!");
+    }
+
+    bool match = false;
+    for (const auto& option : options) {
+        if (option == var_value || (option.back() == '*' &&
+                                    !var_value.compare(0, option.length() - 1, option, 0,
+                                                       option.length() - 1))) {
+            match = true;
+            break;
+        }
+    }
+
+    if (invert) {
+        match = !match;
+    }
+
+    if (match) {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
+        return;
+    }
+
+    fprintf(stderr, "FAILED\n\n");
+    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
+    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", options[0].c_str());
+    for (auto it = std::next(options.begin()); it != options.end(); ++it) {
+        fprintf(stderr, " or '%s'", it->c_str());
+    }
+    fprintf(stderr, ".\n\n");
+    die("requirements not met!");
 }
 
-#define MAX_OPTIONS 32
-static void check_requirement(char* line) {
-    char *val[MAX_OPTIONS];
-    unsigned count;
-    char *x;
-    int invert = 0;
-
+bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                          bool* invert, std::vector<std::string>* options) {
     // "require product=alpha|beta|gamma"
     // "require version-bootloader=1234"
     // "require-for-product:gamma version-bootloader=istanbul|constantinople"
     // "require partition-exists=vendor"
+    *product = "";
+    *invert = false;
 
-    char* name = line;
-    const char* product = "";
-    if (!strncmp(name, "reject ", 7)) {
-        name += 7;
-        invert = 1;
-    } else if (!strncmp(name, "require ", 8)) {
-        name += 8;
-        invert = 0;
-    } else if (!strncmp(name, "require-for-product:", 20)) {
-        // Get the product and point name past it
-        product = name + 20;
-        name = strchr(name, ' ');
-        if (!name) die("android-info.txt syntax error: %s", line);
-        *name = 0;
-        name += 1;
-        invert = 0;
+    auto require_reject_regex = std::regex{"(require\\s+|reject\\s+)?\\s*(\\S+)\\s*=\\s*(.*)"};
+    auto require_product_regex =
+            std::regex{"require-for-product:\\s*(\\S+)\\s+(\\S+)\\s*=\\s*(.*)"};
+    std::smatch match_results;
+
+    if (std::regex_match(line, match_results, require_reject_regex)) {
+        *invert = Trim(match_results[1]) == "reject";
+    } else if (std::regex_match(line, match_results, require_product_regex)) {
+        *product = match_results[1];
+    } else {
+        return false;
     }
 
-    x = strchr(name, '=');
-    if (x == 0) return;
-    *x = 0;
-    val[0] = x + 1;
+    *name = match_results[2];
+    // Work around an unfortunate name mismatch.
+    if (*name == "board") {
+        *name = "product";
+    }
 
-    name = strip(name);
+    auto raw_options = Split(match_results[3], "|");
+    for (const auto& option : raw_options) {
+        auto trimmed_option = Trim(option);
+        options->emplace_back(trimmed_option);
+    }
 
-    // "require partition-exists=x" is a special case, added because of the trouble we had when
-    // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
-    // missing out new partitions. A device with new partitions can use "partition-exists" to
-    // override the fields `optional_if_no_image` in the `images` array.
-    if (!strcmp(name, "partition-exists")) {
-        const char* partition_name = val[0];
-        std::string has_slot;
-        if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
-            (has_slot != "yes" && has_slot != "no")) {
-            die("device doesn't have required partition %s!", partition_name);
+    return true;
+}
+
+// "require partition-exists=x" is a special case, added because of the trouble we had when
+// Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
+// missing out new partitions. A device with new partitions can use "partition-exists" to
+// override the fields `optional_if_no_image` in the `images` array.
+static void HandlePartitionExists(const std::vector<std::string>& options) {
+    const std::string& partition_name = options[0];
+    std::string has_slot;
+    if (fb->GetVar("has-slot:" + partition_name, &has_slot) != fastboot::SUCCESS ||
+        (has_slot != "yes" && has_slot != "no")) {
+        die("device doesn't have required partition %s!", partition_name.c_str());
+    }
+    bool known_partition = false;
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        if (images[i].nickname && images[i].nickname == partition_name) {
+            images[i].optional_if_no_image = false;
+            known_partition = true;
         }
-        bool known_partition = false;
-        for (size_t i = 0; i < arraysize(images); ++i) {
-            if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
-                images[i].optional_if_no_image = false;
-                known_partition = true;
-            }
+    }
+    if (!known_partition) {
+        die("device requires partition %s which is not known to this version of fastboot",
+            partition_name.c_str());
+    }
+}
+
+static void CheckRequirements(const std::string& data) {
+    std::string cur_product;
+    if (fb->GetVar("product", &cur_product) != fastboot::SUCCESS) {
+        fprintf(stderr, "getvar:product FAILED (%s)\n", fb->Error().c_str());
+    }
+
+    auto lines = Split(data, "\n");
+    for (const auto& line : lines) {
+        if (line.empty()) {
+            continue;
         }
-        if (!known_partition) {
-            die("device requires partition %s which is not known to this version of fastboot",
-                partition_name);
+
+        std::string name;
+        std::string product;
+        bool invert;
+        std::vector<std::string> options;
+
+        if (!ParseRequirementLine(line, &name, &product, &invert, &options)) {
+            fprintf(stderr, "android-info.txt syntax error: %s\n", line.c_str());
+            continue;
         }
+        if (name == "partition-exists") {
+            HandlePartitionExists(options);
+        } else {
+            CheckRequirement(cur_product, name, product, invert, options);
+        }
+    }
+}
+
+static void DisplayVarOrError(const std::string& label, const std::string& var) {
+    std::string value;
+
+    if (fb->GetVar(var, &value) != fastboot::SUCCESS) {
+        Status("getvar:" + var);
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
         return;
     }
-
-    for(count = 1; count < MAX_OPTIONS; count++) {
-        x = strchr(val[count - 1],'|');
-        if (x == 0) break;
-        *x = 0;
-        val[count] = x + 1;
-    }
-
-    // Work around an unfortunate name mismatch.
-    const char* var = name;
-    if (!strcmp(name, "board")) var = "product";
-
-    const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
-    if (out == nullptr) die("out of memory");
-
-    for (size_t i = 0; i < count; ++i) {
-        out[i] = xstrdup(strip(val[i]));
-    }
-
-    fb_require(product, var, invert, count, out);
+    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
 }
 
-static void check_requirements(char* data, int64_t sz) {
-    char* s = data;
-    while (sz-- > 0) {
-        if (*s == '\n') {
-            *s++ = 0;
-            check_requirement(data);
-            data = s;
-        } else {
-            s++;
-        }
-    }
-}
+static void DumpInfo() {
+    fprintf(stderr, "--------------------------------------------\n");
+    DisplayVarOrError("Bootloader Version...", "version-bootloader");
+    DisplayVarOrError("Baseband Version.....", "version-baseband");
+    DisplayVarOrError("Serial Number........", "serialno");
+    fprintf(stderr, "--------------------------------------------\n");
 
-static void dump_info() {
-    fb_notice("--------------------------------------------");
-    fb_display("Bootloader Version...", "version-bootloader");
-    fb_display("Baseband Version.....", "version-baseband");
-    fb_display("Serial Number........", "serialno");
-    fb_notice("--------------------------------------------");
 }
 
 static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
@@ -734,7 +802,7 @@
 
 static int64_t get_target_sparse_limit() {
     std::string max_download_size;
-    if (!fb_getvar("max-download-size", &max_download_size) ||
+    if (fb->GetVar("max-download-size", &max_download_size) != fastboot::SUCCESS ||
         max_download_size.empty()) {
         verbose("target didn't report max-download-size");
         return 0;
@@ -882,12 +950,12 @@
 
             for (size_t i = 0; i < sparse_files.size(); ++i) {
                 const auto& pair = sparse_files[i];
-                fb_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
+                fb->FlashPartition(partition, pair.first, pair.second, i + 1, sparse_files.size());
             }
             break;
         }
         case FB_BUFFER_FD:
-            fb_flash_fd(partition, buf->fd, buf->sz);
+            fb->FlashPartition(partition, buf->fd, buf->sz);
             break;
         default:
             die("unknown buffer type: %d", buf->type);
@@ -896,14 +964,15 @@
 
 static std::string get_current_slot() {
     std::string current_slot;
-    if (!fb_getvar("current-slot", &current_slot)) return "";
+    if (fb->GetVar("current-slot", &current_slot) != fastboot::SUCCESS) return "";
     return current_slot;
 }
 
 static int get_slot_count() {
     std::string var;
     int count = 0;
-    if (!fb_getvar("slot-count", &var) || !android::base::ParseInt(var, &count)) {
+    if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
+        !android::base::ParseInt(var, &count)) {
         return 0;
     }
     return count;
@@ -978,7 +1047,7 @@
     std::string has_slot;
     std::string current_slot;
 
-    if (!fb_getvar("has-slot:" + part, &has_slot)) {
+    if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
         /* If has-slot is not supported, the answer is no. */
         has_slot = "no";
     }
@@ -1011,7 +1080,7 @@
     std::string has_slot;
 
     if (slot == "all") {
-        if (!fb_getvar("has-slot:" + part, &has_slot)) {
+        if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
             die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
         }
         if (has_slot == "yes") {
@@ -1041,25 +1110,25 @@
     if (!supports_AB()) return;
 
     if (slot_override != "") {
-        fb_set_active(slot_override);
+        fb->SetActive(slot_override);
     } else {
         std::string current_slot = get_current_slot();
         if (current_slot != "") {
-            fb_set_active(current_slot);
+            fb->SetActive(current_slot);
         }
     }
 }
 
 static bool is_userspace_fastboot() {
     std::string value;
-    return fb_getvar("is-userspace", &value) && value == "yes";
+    return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
 }
 
 static bool if_partition_exists(const std::string& partition, const std::string& slot) {
     std::string has_slot;
     std::string partition_name = partition;
 
-    if (fb_getvar("has-slot:" + partition, &has_slot) && has_slot == "yes") {
+    if (fb->GetVar("has-slot:" + partition, &has_slot) == fastboot::SUCCESS && has_slot == "yes") {
         if (slot == "") {
             std::string current_slot = get_current_slot();
             if (current_slot == "") {
@@ -1071,28 +1140,29 @@
         }
     }
     std::string partition_size;
-    return fb_getvar("partition-size:" + partition_name, &partition_size);
+    return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
 }
 
 static bool is_logical(const std::string& partition) {
     std::string value;
-    return fb_getvar("is-logical:" + partition, &value) && value == "yes";
+    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
 }
 
 static void reboot_to_userspace_fastboot() {
-    if (!fb_reboot_to_userspace()) {
-        die("Must reboot to userspace fastboot to flash logical partitions");
-    }
+    fb->RebootTo("fastboot");
+
+    auto* old_transport = fb->set_transport(nullptr);
+    delete old_transport;
 
     // Give the current connection time to close.
     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 
-    fb_reinit(open_device());
+    fb->set_transport(open_device());
 }
 
 class ImageSource {
   public:
-    virtual void* ReadFile(const std::string& name, int64_t* size) const = 0;
+    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
     virtual int OpenFile(const std::string& name) const = 0;
 };
 
@@ -1128,6 +1198,7 @@
 }
 
 void FlashAllTool::Flash() {
+    DumpInfo();
     CheckRequirements();
     DetermineSecondarySlot();
     CollectImages();
@@ -1144,7 +1215,7 @@
     for (const auto& [image, slot] : os_images_) {
         auto resize_partition = [](const std::string& partition) -> void {
             if (is_logical(partition)) {
-                fb_resize_partition(partition, "0");
+                fb->ResizePartition(partition, "0");
             }
         };
         do_for_partitions(image->part_name, slot, resize_partition, false);
@@ -1161,12 +1232,11 @@
 }
 
 void FlashAllTool::CheckRequirements() {
-    int64_t sz;
-    void* data = source_.ReadFile("android-info.txt", &sz);
-    if (data == nullptr) {
+    std::vector<char> contents;
+    if (!source_.ReadFile("android-info.txt", &contents)) {
         die("could not read android-info.txt");
     }
-    check_requirements(reinterpret_cast<char*>(data), sz);
+    ::CheckRequirements({contents.data(), contents.size()});
 }
 
 void FlashAllTool::DetermineSecondarySlot() {
@@ -1219,15 +1289,14 @@
 
 void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
     auto flash = [&, this](const std::string& partition_name) {
-        int64_t sz;
-        void* data = source_.ReadFile(image.sig_name, &sz);
-        if (data) {
-            fb_download("signature", data, sz);
-            fb_command("signature", "installing signature");
+        std::vector<char> signature_data;
+        if (source_.ReadFile(image.sig_name, &signature_data)) {
+            fb->Download("signature", signature_data);
+            fb->RawCommand("signature", "installing signature");
         }
 
         if (is_logical(partition_name)) {
-            fb_resize_partition(partition_name, std::to_string(buf->image_size));
+            fb->ResizePartition(partition_name, std::to_string(buf->image_size));
         }
         flash_buf(partition_name.c_str(), buf);
     };
@@ -1246,27 +1315,27 @@
     if (!is_userspace_fastboot()) {
         reboot_to_userspace_fastboot();
     }
-    fb_download_fd("super", fd, get_file_size(fd));
+    fb->Download("super", fd, get_file_size(fd));
 
     std::string command = "update-super:super";
     if (wipe_) {
         command += ":wipe";
     }
-    fb_command(command, "Updating super partition");
+    fb->RawCommand(command, "Updating super partition");
 }
 
 class ZipImageSource final : public ImageSource {
   public:
     explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
-    void* ReadFile(const std::string& name, int64_t* size) const override;
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
     int OpenFile(const std::string& name) const override;
 
   private:
     ZipArchiveHandle zip_;
 };
 
-void* ZipImageSource::ReadFile(const std::string& name, int64_t* size) const {
-    return unzip_to_memory(zip_, name.c_str(), size);
+bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+    return UnzipToMemory(zip_, name, out);
 }
 
 int ZipImageSource::OpenFile(const std::string& name) const {
@@ -1274,10 +1343,6 @@
 }
 
 static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
-    dump_info();
-
-    fb_query_save("product", cur_product, sizeof(cur_product));
-
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
@@ -1292,16 +1357,16 @@
 
 class LocalImageSource final : public ImageSource {
   public:
-    void* ReadFile(const std::string& name, int64_t* size) const override;
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
     int OpenFile(const std::string& name) const override;
 };
 
-void* LocalImageSource::ReadFile(const std::string& name, int64_t* size) const {
+bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
     auto path = find_item_given_name(name);
     if (path.empty()) {
-        return nullptr;
+        return false;
     }
-    return load_file(path.c_str(), size);
+    return ReadFileToVector(path, out);
 }
 
 int LocalImageSource::OpenFile(const std::string& name) const {
@@ -1310,11 +1375,6 @@
 }
 
 static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
-    std::string fname;
-    dump_info();
-
-    fb_query_save("product", cur_product, sizeof(cur_product));
-
     FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
     tool.Flash();
 }
@@ -1333,7 +1393,7 @@
     while (!args->empty()) {
         command += " " + next_arg(args);
     }
-    fb_command(command, "");
+    fb->RawCommand(command, "");
 }
 
 static std::string fb_fix_numeric_var(std::string var) {
@@ -1347,7 +1407,7 @@
 
 static unsigned fb_get_flash_block_size(std::string name) {
     std::string sizeString;
-    if (!fb_getvar(name, &sizeString) || sizeString.empty()) {
+    if (fb->GetVar(name, &sizeString) != fastboot::SUCCESS || sizeString.empty()) {
         // This device does not report flash block sizes, so return 0.
         return 0;
     }
@@ -1385,7 +1445,7 @@
         limit = sparse_limit;
     }
 
-    if (!fb_getvar("partition-type:" + partition, &partition_type)) {
+    if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
@@ -1397,7 +1457,7 @@
         partition_type = type_override;
     }
 
-    if (!fb_getvar("partition-size:" + partition, &partition_size)) {
+    if (fb->GetVar("partition-size:" + partition, &partition_size) != fastboot::SUCCESS) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
@@ -1455,7 +1515,7 @@
         fprintf(stderr, "Erase successful, but not automatically formatting.\n");
         if (errMsg) fprintf(stderr, "%s", errMsg);
     }
-    fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
+    fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
 }
 
 int FastBootTool::Main(int argc, char* argv[]) {
@@ -1468,8 +1528,6 @@
     bool wants_set_active = false;
     bool skip_secondary = false;
     bool set_fbe_marker = false;
-    void *data;
-    int64_t sz;
     int longindex;
     std::string slot_override;
     std::string next_active;
@@ -1607,8 +1665,13 @@
     if (transport == nullptr) {
         return 1;
     }
-    fastboot::FastBootDriver fb(transport);
-    fb_init(fb);
+    fastboot::DriverCallbacks driver_callbacks = {
+        .prolog = Status,
+        .epilog = Epilog,
+        .info = InfoMessage,
+    };
+    fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+    fb = &fastboot_driver;
 
     const double start = now();
 
@@ -1619,7 +1682,7 @@
         if (next_active == "") {
             if (slot_override == "") {
                 std::string current_slot;
-                if (fb_getvar("current-slot", &current_slot)) {
+                if (fb->GetVar("current-slot", &current_slot) == fastboot::SUCCESS) {
                     next_active = verify_slot(current_slot, false);
                 } else {
                     wants_set_active = false;
@@ -1636,19 +1699,18 @@
 
         if (command == "getvar") {
             std::string variable = next_arg(&args);
-            fb_display(variable, variable);
+            DisplayVarOrError(variable, variable);
         } else if (command == "erase") {
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
                 std::string partition_type;
-                if (fb_getvar(std::string("partition-type:") + partition,
-                              &partition_type) &&
+                if (fb->GetVar("partition-type:" + partition, &partition_type) == fastboot::SUCCESS &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
                             partition_type.c_str());
                 }
 
-                fb_erase(partition);
+                fb->Erase(partition);
             };
             do_for_partitions(partition, slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
@@ -1673,11 +1735,13 @@
             do_for_partitions(partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
-            data = load_file(filename.c_str(), &sz);
-            if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
-            if (sz != 256) die("signature must be 256 bytes (got %" PRId64 ")", sz);
-            fb_download("signature", data, sz);
-            fb_command("signature", "installing signature");
+            std::vector<char> data;
+            if (!ReadFileToVector(filename, &data)) {
+                die("could not load '%s': %s", filename.c_str(), strerror(errno));
+            }
+            if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
+            fb->Download("signature", data);
+            fb->RawCommand("signature", "installing signature");
         } else if (command == "reboot") {
             wants_reboot = true;
 
@@ -1705,7 +1769,7 @@
         } else if (command == "reboot-fastboot") {
             wants_reboot_fastboot = true;
         } else if (command == "continue") {
-            fb_command("continue", "resuming boot");
+            fb->Continue();
         } else if (command == "boot") {
             std::string kernel = next_arg(&args);
             std::string ramdisk;
@@ -1713,9 +1777,9 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            fb_download("boot.img", data, sz);
-            fb_command("boot", "booting");
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            fb->Download("boot.img", data);
+            fb->Boot();
         } else if (command == "flash") {
             std::string pname = next_arg(&args);
 
@@ -1739,9 +1803,9 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            auto flashraw = [&](const std::string& partition) {
-                fb_flash(partition, data, sz);
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            auto flashraw = [&data](const std::string& partition) {
+                fb->FlashPartition(partition, data);
             };
             do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
@@ -1765,7 +1829,7 @@
             wants_reboot = true;
         } else if (command == "set_active") {
             std::string slot = verify_slot(next_arg(&args), false);
-            fb_set_active(slot);
+            fb->SetActive(slot);
         } else if (command == "stage") {
             std::string filename = next_arg(&args);
 
@@ -1773,10 +1837,10 @@
             if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
-            fb_download_fd(filename, buf.fd, buf.sz);
+            fb->Download(filename, buf.fd, buf.sz);
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
-            fb_upload(filename);
+            fb->Upload(filename);
         } else if (command == "oem") {
             do_oem_command("oem", &args);
         } else if (command == "flashing") {
@@ -1793,14 +1857,14 @@
         } else if (command == "create-logical-partition") {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb_create_partition(partition, size);
+            fb->CreatePartition(partition, size);
         } else if (command == "delete-logical-partition") {
             std::string partition = next_arg(&args);
-            fb_delete_partition(partition);
+            fb->DeletePartition(partition);
         } else if (command == "resize-logical-partition") {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb_resize_partition(partition, size);
+            fb->ResizePartition(partition, size);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
@@ -1810,9 +1874,11 @@
         std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
         for (const auto& partition : partitions) {
             std::string partition_type;
-            if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
+            if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
+                continue;
+            }
             if (partition_type.empty()) continue;
-            fb_erase(partition);
+            fb->Erase(partition);
             if (partition == "userdata" && set_fbe_marker) {
                 fprintf(stderr, "setting FBE marker on initial userdata...\n");
                 std::string initial_userdata_dir = create_fbemarker_tmpdir();
@@ -1824,27 +1890,27 @@
         }
     }
     if (wants_set_active) {
-        fb_set_active(next_active);
+        fb->SetActive(next_active);
     }
     if (wants_reboot && !skip_reboot) {
-        fb_reboot();
-        fb_wait_for_disconnect();
+        fb->Reboot();
+        fb->WaitForDisconnect();
     } else if (wants_reboot_bootloader) {
-        fb_command("reboot-bootloader", "rebooting into bootloader");
-        fb_wait_for_disconnect();
+        fb->RebootTo("bootloader");
+        fb->WaitForDisconnect();
     } else if (wants_reboot_recovery) {
-        fb_command("reboot-recovery", "rebooting into recovery");
-        fb_wait_for_disconnect();
+        fb->RebootTo("recovery");
+        fb->WaitForDisconnect();
     } else if (wants_reboot_fastboot) {
-        fb_command("reboot-fastboot", "rebooting into fastboot");
-        fb_wait_for_disconnect();
+        fb->RebootTo("fastboot");
+        fb->WaitForDisconnect();
     }
 
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
 
-    if (Transport* old_transport = fb.set_transport(nullptr)) {
-        delete old_transport;
-    }
+    auto* old_transport = fb->set_transport(nullptr);
+    delete old_transport;
+
     return 0;
 }
 
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
new file mode 100644
index 0000000..9f18253
--- /dev/null
+++ b/fastboot/fastboot.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <bootimg.h>
+
+class FastBootTool {
+  public:
+    int Main(int argc, char* argv[]);
+
+    void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
+    void ParseOsVersion(boot_img_hdr_v1*, const char*);
+};
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 4a14131..b1f3bc9 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -25,6 +25,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #include "fastboot_driver.h"
 
 #include <errno.h>
@@ -44,43 +45,56 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <utils/FileMap.h>
-#include "fastboot_driver.h"
+
+#include "constants.h"
 #include "transport.h"
 
+using android::base::StringPrintf;
+
 namespace fastboot {
 
 /*************************** PUBLIC *******************************/
-FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
+FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
                                bool no_checks)
-    : transport_(transport) {
-    info_cb_ = info;
-    disable_checks_ = no_checks;
-}
+    : transport_(transport),
+      prolog_(std::move(driver_callbacks.prolog)),
+      epilog_(std::move(driver_callbacks.epilog)),
+      info_(std::move(driver_callbacks.info)),
+      disable_checks_(no_checks) {}
 
 FastBootDriver::~FastBootDriver() {
 }
 
 RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
-    return RawCommand(Commands::BOOT, response, info);
+    return RawCommand(FB_CMD_BOOT, "Booting", response, info);
 }
 
 RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
-    return RawCommand(Commands::CONTINUE, response, info);
+    return RawCommand(FB_CMD_CONTINUE, "Resuming boot", response, info);
 }
 
-RetCode FastBootDriver::Erase(const std::string& part, std::string* response,
-                              std::vector<std::string>* info) {
-    return RawCommand(Commands::ERASE + part, response, info);
+RetCode FastBootDriver::CreatePartition(const std::string& partition, const std::string& size) {
+    return RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size,
+                      "Creating '" + partition + "'");
 }
 
-RetCode FastBootDriver::Flash(const std::string& part, std::string* response,
+RetCode FastBootDriver::DeletePartition(const std::string& partition) {
+    return RawCommand(FB_CMD_DELETE_PARTITION ":" + partition, "Deleting '" + partition + "'");
+}
+
+RetCode FastBootDriver::Erase(const std::string& partition, std::string* response,
                               std::vector<std::string>* info) {
-    return RawCommand(Commands::FLASH + part, response, info);
+    return RawCommand(FB_CMD_ERASE ":" + partition, "Erasing '" + partition + "'", response, info);
+}
+
+RetCode FastBootDriver::Flash(const std::string& partition, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(FB_CMD_FLASH ":" + partition, "Writing '" + partition + "'", response, info);
 }
 
 RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
                                std::vector<std::string>* info) {
-    return RawCommand(Commands::GET_VAR + key, val, info);
+    return RawCommand(FB_CMD_GETVAR ":" + key, val, info);
 }
 
 RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
@@ -89,44 +103,52 @@
 }
 
 RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
-    return RawCommand(Commands::REBOOT, response, info);
+    return RawCommand(FB_CMD_REBOOT, "Rebooting", response, info);
 }
 
 RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
                                  std::vector<std::string>* info) {
-    return RawCommand("reboot-" + target, response, info);
+    return RawCommand("reboot-" + target, "Rebooting into " + target, response, info);
+}
+
+RetCode FastBootDriver::ResizePartition(const std::string& partition, const std::string& size) {
+    return RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size,
+                      "Resizing '" + partition + "'");
 }
 
 RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
                                   std::vector<std::string>* info) {
-    return RawCommand(Commands::SET_ACTIVE + slot, response, info);
+    return RawCommand(FB_CMD_SET_ACTIVE ":" + slot, "Setting current slot to '" + slot + "'",
+                      response, info);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition,
+                                       const std::vector<char>& data) {
     RetCode ret;
-    if ((ret = Download(data))) {
+    if ((ret = Download(partition, data))) {
         return ret;
     }
-    return RawCommand(Commands::FLASH + part);
+    return Flash(partition);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& part, int fd, uint32_t sz) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
     RetCode ret;
-    if ((ret = Download(fd, sz))) {
+    if ((ret = Download(partition, fd, size))) {
         return ret;
     }
-    return RawCommand(Commands::FLASH + part);
+    return Flash(partition);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& part, sparse_file* s) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, sparse_file* s, uint32_t size,
+                                       size_t current, size_t total) {
     RetCode ret;
-    if ((ret = Download(s))) {
+    if ((ret = Download(partition, s, size, current, total, false))) {
         return ret;
     }
-    return RawCommand(Commands::FLASH + part);
+    return Flash(partition);
 }
 
-RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* parts) {
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions) {
     std::vector<std::string> all;
     RetCode ret;
     if ((ret = GetVarAll(&all))) {
@@ -141,32 +163,18 @@
             std::string m1(sm[1]);
             std::string m2(sm[2]);
             uint64_t tmp = strtoll(m2.c_str(), 0, 16);
-            parts->push_back(std::make_tuple(m1, tmp));
+            partitions->push_back(std::make_tuple(m1, tmp));
         }
     }
     return SUCCESS;
 }
 
-RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
-                                bool* reqmet, bool invert) {
-    *reqmet = invert;
-    RetCode ret;
-    std::string response;
-    if ((ret = GetVar(var, &response))) {
-        return ret;
-    }
-
-    // Now check if we have a match
-    for (const auto s : allowed) {
-        // If it ends in *, and starting substring match
-        if (response == s || (s.length() && s.back() == '*' &&
-                              !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
-            *reqmet = !invert;
-            break;
-        }
-    }
-
-    return SUCCESS;
+RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
+                                 std::string* response, std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
+    auto result = Download(fd, size, response, info);
+    epilog_(result);
+    return result;
 }
 
 RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
@@ -192,26 +200,29 @@
     return HandleResponse(response, info);
 }
 
-RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
-                                 std::vector<std::string>* info) {
-    return Download(buf.data(), buf.size(), response, info);
+RetCode FastBootDriver::Download(const std::string& name, const std::vector<char>& buf,
+                                 std::string* response, std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), buf.size() / 1024));
+    auto result = Download(buf, response, info);
+    epilog_(result);
+    return result;
 }
 
-RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
+RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
     error_ = "";
-    if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+    if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
         error_ = "Buffer is too large or 0 bytes";
         return BAD_ARG;
     }
 
-    if ((ret = DownloadCommand(size, response, info))) {
+    if ((ret = DownloadCommand(buf.size(), response, info))) {
         return ret;
     }
 
     // Write the buffer
-    if ((ret = SendBuffer(buf, size))) {
+    if ((ret = SendBuffer(buf))) {
         return ret;
     }
 
@@ -219,6 +230,16 @@
     return HandleResponse(response, info);
 }
 
+RetCode FastBootDriver::Download(const std::string& partition, struct sparse_file* s, uint32_t size,
+                                 size_t current, size_t total, bool use_crc, std::string* response,
+                                 std::vector<std::string>* info) {
+    prolog_(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
+                         size / 1024));
+    auto result = Download(s, use_crc, response, info);
+    epilog_(result);
+    return result;
+}
+
 RetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,
                                  std::vector<std::string>* info) {
     error_ = "";
@@ -261,9 +282,17 @@
 
 RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
                                std::vector<std::string>* info) {
+    prolog_("Uploading '" + outfile + "'");
+    auto result = UploadInner(outfile, response, info);
+    epilog_(result);
+    return result;
+}
+
+RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
+                                    std::vector<std::string>* info) {
     RetCode ret;
     int dsize;
-    if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize))) {
+    if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
         error_ = "Upload request failed: " + error_;
         return ret;
     }
@@ -297,8 +326,8 @@
 }
 
 // Helpers
-void FastBootDriver::SetInfoCallback(std::function<void(std::string&)> info) {
-    info_cb_ = info;
+void FastBootDriver::SetInfoCallback(std::function<void(const std::string&)> info) {
+    info_ = info;
 }
 
 const std::string FastBootDriver::RCString(RetCode rc) {
@@ -335,6 +364,15 @@
 }
 
 /****************************** PROTECTED *************************************/
+RetCode FastBootDriver::RawCommand(const std::string& cmd, const std::string& message,
+                                   std::string* response, std::vector<std::string>* info,
+                                   int* dsize) {
+    prolog_(message);
+    auto result = RawCommand(cmd, response, info, dsize);
+    epilog_(result);
+    return result;
+}
+
 RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
                                    std::vector<std::string>* info, int* dsize) {
     error_ = "";  // Clear any pending error
@@ -354,7 +392,7 @@
 
 RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
                                         std::vector<std::string>* info) {
-    std::string cmd(android::base::StringPrintf("%s%08" PRIx32, Commands::DOWNLOAD.c_str(), size));
+    std::string cmd(android::base::StringPrintf("%s:%08" PRIx32, FB_CMD_DOWNLOAD, size));
     RetCode ret;
     if ((ret = RawCommand(cmd, response, info))) {
         return ret;
@@ -387,7 +425,7 @@
         std::string input(status);
         if (android::base::StartsWith(input, "INFO")) {
             std::string tmp = input.substr(strlen("INFO"));
-            info_cb_(tmp);
+            info_(tmp);
             add_info(std::move(tmp));
         } else if (android::base::StartsWith(input, "OKAY")) {
             set_response(input.substr(strlen("OKAY")));
@@ -420,16 +458,6 @@
     return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
 }
 
-const std::string FastBootDriver::Commands::BOOT = "boot";
-const std::string FastBootDriver::Commands::CONTINUE = "continue";
-const std::string FastBootDriver::Commands::DOWNLOAD = "download:";
-const std::string FastBootDriver::Commands::ERASE = "erase:";
-const std::string FastBootDriver::Commands::FLASH = "flash:";
-const std::string FastBootDriver::Commands::GET_VAR = "getvar:";
-const std::string FastBootDriver::Commands::REBOOT = "reboot";
-const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
-const std::string FastBootDriver::Commands::UPLOAD = "upload";
-
 /******************************* PRIVATE **************************************/
 RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
     static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 4d85ba0..62bbe52 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -55,6 +55,12 @@
     TIMEOUT,
 };
 
+struct DriverCallbacks {
+    std::function<void(const std::string&)> prolog = [](const std::string&) {};
+    std::function<void(int)> epilog = [](int) {};
+    std::function<void(const std::string&)> info = [](const std::string&) {};
+};
+
 class FastBootDriver {
     friend class FastBootTest;
 
@@ -63,25 +69,30 @@
     static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
     static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
 
-    FastBootDriver(Transport* transport,
-                   std::function<void(std::string&)> info = [](std::string&) {},
+    FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks = {},
                    bool no_checks = false);
     ~FastBootDriver();
 
     RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode CreatePartition(const std::string& partition, const std::string& size);
+    RetCode DeletePartition(const std::string& partition);
+    RetCode Download(const std::string& name, int fd, size_t size, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
     RetCode Download(int fd, size_t size, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
+    RetCode Download(const std::string& name, const std::vector<char>& buf,
+                     std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
-    // This will be removed after fastboot is modified to use a vector
-    RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
+    RetCode Download(const std::string& partition, struct sparse_file* s, uint32_t sz,
+                     size_t current, size_t total, bool use_crc, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
-    RetCode Erase(const std::string& part, std::string* response = nullptr,
+    RetCode Erase(const std::string& partition, std::string* response = nullptr,
                   std::vector<std::string>* info = nullptr);
-    RetCode Flash(const std::string& part, std::string* response = nullptr,
+    RetCode Flash(const std::string& partition, std::string* response = nullptr,
                   std::vector<std::string>* info = nullptr);
     RetCode GetVar(const std::string& key, std::string* val,
                    std::vector<std::string>* info = nullptr);
@@ -89,22 +100,24 @@
     RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode RebootTo(std::string target, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
+    RetCode ResizePartition(const std::string& partition, const std::string& size);
     RetCode SetActive(const std::string& slot, std::string* response = nullptr,
                       std::vector<std::string>* info = nullptr);
     RetCode Upload(const std::string& outfile, std::string* response = nullptr,
                    std::vector<std::string>* info = nullptr);
 
     /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
-    RetCode FlashPartition(const std::string& part, const std::vector<char>& data);
-    RetCode FlashPartition(const std::string& part, int fd, uint32_t sz);
-    RetCode FlashPartition(const std::string& part, sparse_file* s);
+    RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
+    RetCode FlashPartition(const std::string& partition, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
+                           size_t current, size_t total);
 
-    RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* parts);
+    RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions);
     RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
                     bool invert = false);
 
     /* HELPERS */
-    void SetInfoCallback(std::function<void(std::string&)> info);
+    void SetInfoCallback(std::function<void(const std::string&)> info);
     static const std::string RCString(RetCode rc);
     std::string Error();
     RetCode WaitForDisconnect();
@@ -113,7 +126,10 @@
     Transport* set_transport(Transport* transport);
     Transport* transport() const { return transport_; }
 
-    // This is temporarily public for engine.cpp
+    RetCode RawCommand(const std::string& cmd, const std::string& message,
+                       std::string* response = nullptr, std::vector<std::string>* info = nullptr,
+                       int* dsize = nullptr);
+
     RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
                        std::vector<std::string>* info = nullptr, int* dsize = nullptr);
 
@@ -125,19 +141,6 @@
 
     std::string ErrnoStr(const std::string& msg);
 
-    // More like a namespace...
-    struct Commands {
-        static const std::string BOOT;
-        static const std::string CONTINUE;
-        static const std::string DOWNLOAD;
-        static const std::string ERASE;
-        static const std::string FLASH;
-        static const std::string GET_VAR;
-        static const std::string REBOOT;
-        static const std::string SET_ACTIVE;
-        static const std::string UPLOAD;
-    };
-
     Transport* transport_;
 
   private:
@@ -148,10 +151,15 @@
     RetCode ReadBuffer(std::vector<char>& buf);
     RetCode ReadBuffer(void* buf, size_t size);
 
+    RetCode UploadInner(const std::string& outfile, std::string* response = nullptr,
+                        std::vector<std::string>* info = nullptr);
+
     int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
 
     std::string error_;
-    std::function<void(std::string&)> info_cb_;
+    std::function<void(const std::string&)> prolog_;
+    std::function<void(int)> epilog_;
+    std::function<void(const std::string&)> info_;
     bool disable_checks_;
 };
 
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 43201fa..9c3ab6e 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "engine.h"
+#include "fastboot.h"
 
 #include <gtest/gtest.h>
 
@@ -59,3 +59,145 @@
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.128.3"), "bad OS version");
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.2.128"), "bad OS version");
 }
+
+extern bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                                 bool* invert, std::vector<std::string>* options);
+
+static void ParseRequirementLineTest(const std::string& line, const std::string& expected_name,
+                                     const std::string& expected_product, bool expected_invert,
+                                     const std::vector<std::string>& expected_options) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_TRUE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+
+    EXPECT_EQ(expected_name, name) << line;
+    EXPECT_EQ(expected_product, product) << line;
+    EXPECT_EQ(expected_invert, invert) << line;
+    EXPECT_EQ(expected_options, options) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineSuccesses) {
+    // Examples provided in the code + slight variations.
+    ParseRequirementLineTest("require product=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require version-bootloader=1234", "version-bootloader", "", false,
+                             {"1234"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul|constantinople",
+                             "version-bootloader", "gamma", false, {"istanbul", "constantinople"});
+    ParseRequirementLineTest("require partition-exists=vendor", "partition-exists", "", false,
+                             {"vendor"});
+    ParseRequirementLineTest("reject product=alpha", "product", "", true, {"alpha"});
+    ParseRequirementLineTest("reject product=alpha|beta|gamma", "product", "", true,
+                             {"alpha", "beta", "gamma"});
+
+    // Without any prefix, assume 'require'
+    ParseRequirementLineTest("product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    // Including if the variable name is otherwise a prefix keyword
+    ParseRequirementLineTest("require = alpha", "require", "", false, {"alpha"});
+    ParseRequirementLineTest("reject = alpha", "reject", "", false, {"alpha"});
+    ParseRequirementLineTest("require-for-product:gamma = alpha", "require-for-product:gamma", "",
+                             false, {"alpha"});
+
+    // Extra spaces are allowed.
+    ParseRequirementLineTest("require    product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product    =alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product   =   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("product  =  alpha  |  beta  |  gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require-for-product:  gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+
+    // Extraneous ending | is okay, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha|", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta|gamma|", "product", "", false,
+                             {"alpha", "beta", "gamma", ""});
+
+    // Accept empty options, double ||, etc, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha||beta|   |gamma", "product", "", false,
+                             {"alpha", "", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||beta|gamma", "product", "", false,
+                             {"alpha", "", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|   |gamma", "product", "", false,
+                             {"alpha", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha|| ", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha| ", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta| ", "product", "", false,
+                             {"alpha", "beta", ""});
+
+    // No option string is also treating as accepting an empty string.
+    ParseRequirementLineTest("require =", "require", "", false, {""});
+    ParseRequirementLineTest("require = |", "require", "", false, {"", ""});
+    ParseRequirementLineTest("reject =", "reject", "", false, {""});
+    ParseRequirementLineTest("reject = |", "reject", "", false, {"", ""});
+    ParseRequirementLineTest("require-for-product: =", "require-for-product:", "", false, {""});
+    ParseRequirementLineTest("require-for-product: = | ", "require-for-product:", "", false,
+                             {"", ""});
+    ParseRequirementLineTest("require product=", "product", "", false, {""});
+    ParseRequirementLineTest("require product = ", "product", "", false, {""});
+    ParseRequirementLineTest("require product = | ", "product", "", false, {"", ""});
+    ParseRequirementLineTest("reject product=", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = ", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = | ", "product", "", true, {"", ""});
+    ParseRequirementLineTest("require-for-product:gamma product=", "product", "gamma", false, {""});
+    ParseRequirementLineTest("require-for-product:gamma product = ", "product", "gamma", false,
+                             {""});
+    ParseRequirementLineTest("require-for-product:gamma product = |", "product", "gamma", false,
+                             {"", ""});
+
+    // Check for board -> product substitution.
+    ParseRequirementLineTest("require board=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("board=alpha", "product", "", false, {"alpha"});
+}
+
+static void ParseRequirementLineTestMalformed(const std::string& line) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_FALSE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineMalformed) {
+    ParseRequirementLineTestMalformed("nothing");
+    ParseRequirementLineTestMalformed("");
+    ParseRequirementLineTestMalformed("=");
+    ParseRequirementLineTestMalformed("|");
+
+    ParseRequirementLineTestMalformed("require");
+    ParseRequirementLineTestMalformed("require ");
+    ParseRequirementLineTestMalformed("reject");
+    ParseRequirementLineTestMalformed("reject ");
+    ParseRequirementLineTestMalformed("require-for-product:");
+    ParseRequirementLineTestMalformed("require-for-product: ");
+
+    ParseRequirementLineTestMalformed("require product");
+    ParseRequirementLineTestMalformed("reject product");
+
+    ParseRequirementLineTestMalformed("require-for-product:gamma");
+    ParseRequirementLineTestMalformed("require-for-product:gamma product");
+
+    // No spaces allowed before between require-for-product and :.
+    ParseRequirementLineTestMalformed("require-for-product :");
+}
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 4da71ca..280cfb6 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -86,6 +86,12 @@
     return true;
 }
 
+bool FastBootTest::UserSpaceFastboot() {
+    std::string value;
+    fb->GetVar("is-userspace", &value);
+    return value == "yes";
+}
+
 RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
                                       std::vector<std::string>* info) {
     return fb->DownloadCommand(size, response, info);
@@ -121,8 +127,7 @@
     } else {
         ASSERT_EQ(device_path, cb_scratch);  // The path can not change
     }
-    fb = std::unique_ptr<FastBootDriver>(
-            new FastBootDriver(transport.get(), [](std::string&) {}, true));
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
 }
 
 void FastBootTest::TearDown() {
@@ -159,6 +164,12 @@
         return;
     }
 
+    // User space fastboot implementations are not allowed to communicate to
+    // secure hardware and hence cannot lock/unlock the device.
+    if (UserSpaceFastboot()) {
+        return;
+    }
+
     std::string resp;
     std::vector<std::string> info;
     // To avoid risk of bricking device, make sure unlock ability is set to 1
@@ -204,8 +215,7 @@
             putchar('.');
         }
         device_path = cb_scratch;
-        fb = std::unique_ptr<FastBootDriver>(
-                new FastBootDriver(transport.get(), [](std::string&) {}, true));
+        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
         if (assert_change) {
             ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
             ASSERT_EQ(resp, unlock ? "yes" : "no")
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index e47d0fd..e0f829e 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -47,6 +47,7 @@
 
     static int MatchFastboot(usb_ifc_info* info, const char* local_serial = nullptr);
     bool UsbStillAvailible();
+    bool UserSpaceFastboot();
 
   protected:
     RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 8fb5a6a..e2076f5 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -289,6 +289,12 @@
 TEST_F(Conformance, UnlockAbility) {
     std::string resp;
     std::vector<std::string> info;
+    // Userspace fastboot implementations do not have a way to get this
+    // information.
+    if (UserSpaceFastboot()) {
+        GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+        return;
+    }
     EXPECT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
             << "'flashing get_unlock_ability' failed";
     // There are two ways this can be reported, through info or the actual response
@@ -412,6 +418,10 @@
     ASSERT_TRUE(resp == "yes" || resp == "no")
             << "Device did not respond with 'yes' or 'no' for getvar:unlocked";
     bool curr = resp == "yes";
+    if (UserSpaceFastboot()) {
+        GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+        return;
+    }
 
     for (int i = 0; i < 2; i++) {
         std::string action = !curr ? "unlock" : "lock";
@@ -441,9 +451,6 @@
         ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
         EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
         EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-                << "Download sparse with crc failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
     }
 }
 
@@ -462,9 +469,6 @@
                 << "Adding data failed to sparse file: " << sparse.Rep();
         EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
         EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-                << "Download sparse with crc failed: " << sparse.Rep();
-        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
     }
 }
 
@@ -474,9 +478,6 @@
     ASSERT_TRUE(*sparse) << "Sparse image creation failed";
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseDownload1) {
@@ -487,9 +488,6 @@
             << "Adding data failed to sparse file: " << sparse.Rep();
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseDownload2) {
@@ -503,9 +501,6 @@
             << "Adding data failed to sparse file: " << sparse.Rep();
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseDownload3) {
@@ -537,9 +532,6 @@
     }
     EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
     EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
-            << "Download sparse with crc failed: " << sparse.Rep();
-    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
 }
 
 TEST_F(Conformance, SparseVersionCheck) {
@@ -559,24 +551,6 @@
     }
 }
 
-TEST_F(Conformance, SparseCRCCheck) {
-    SparseWrapper sparse(4096, 4096);
-    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
-    std::vector<char> buf = RandomBuf(4096);
-    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
-            << "Adding data failed to sparse file: " << sparse.Rep();
-    ASSERT_TRUE(SparseToBuf(*sparse, &buf, true)) << "Sparse buffer creation failed";
-    // Flip a bit in the crc
-    buf.back() = buf.back() ^ 0x01;
-    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
-    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
-    // It can either reject this download or reject it during flash
-    if (HandleResponse() != DEVICE_FAIL) {
-        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
-                << "Flashing an invalid sparse version should fail " << sparse.Rep();
-    }
-}
-
 TEST_F(UnlockPermissions, Download) {
     std::vector<char> buf{'a', 'o', 's', 'p'};
     EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
index c3683f7..35f4218 100644
--- a/fastboot/main.cpp
+++ b/fastboot/main.cpp
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#include "engine.h"
+#include "fastboot.h"
 
 int main(int argc, char* argv[]) {
     FastBootTool fb;
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 125182d..d02b37f 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -69,14 +69,3 @@
     }
     fprintf(stderr, "\n");
 }
-
-void Status(const std::string& message) {
-    static constexpr char kStatusFormat[] = "%-50s ";
-    fprintf(stderr, kStatusFormat, message.c_str());
-}
-
-char* xstrdup(const char* s) {
-    char* result = strdup(s);
-    if (!result) die("out of memory");
-    return result;
-}
diff --git a/fastboot/util.h b/fastboot/util.h
index 20be461..2535414 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -9,11 +9,8 @@
 
 /* util stuff */
 double now();
-char* xstrdup(const char*);
 void set_verbose();
 
-void Status(const std::string& message);
-
 // These printf-like functions are implemented in terms of vsnprintf, so they
 // use the same attribute for compile-time format string checking.
 void die(const char* fmt, ...) __attribute__((__noreturn__))
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index f56043c..3ab9732 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -55,6 +55,7 @@
 #include <ext4_utils/wipe.h>
 #include <fs_mgr_overlayfs.h>
 #include <libdm/dm.h>
+#include <liblp/metadata_format.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
@@ -529,8 +530,18 @@
     errno = 0;
     ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
     save_errno = errno;
-    PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
-          << ",type=" << rec->fs_type << ")=" << ret;
+    const char* target_missing = "";
+    const char* source_missing = "";
+    if (save_errno == ENOENT) {
+        if (access(target, F_OK)) {
+            target_missing = "(missing)";
+        } else if (access(source, F_OK)) {
+            source_missing = "(missing)";
+        }
+        errno = save_errno;
+    }
+    PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
+          << target_missing << ",type=" << rec->fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -569,8 +580,7 @@
  *   -1 on failure with errno set to match the 1st mount failure.
  *   0 on success.
  */
-static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
-{
+static int mount_with_alternatives(fstab* fstab, int start_idx, int* end_idx, int* attempted_idx) {
     int i;
     int mount_errno = 0;
     int mounted = 0;
@@ -805,6 +815,23 @@
     return true;
 }
 
+static bool call_vdc_ret(const std::vector<std::string>& args, int* ret) {
+    std::vector<char const*> argv;
+    argv.emplace_back("/system/bin/vdc");
+    for (auto& arg : args) {
+        argv.emplace_back(arg.c_str());
+    }
+    LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
+    int err = android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), ret, false, true);
+    if (err != 0) {
+        LOG(ERROR) << "vdc call failed with error code: " << err;
+        return false;
+    }
+    LOG(DEBUG) << "vdc finished successfully";
+    *ret = WEXITSTATUS(*ret);
+    return true;
+}
+
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
     // Logical partitions are specified with a named partition rather than a
     // block device, so if the block device is a path, then it has already
@@ -823,19 +850,70 @@
     return true;
 }
 
+bool fs_mgr_update_checkpoint_partition(struct fstab_rec* rec) {
+    if (fs_mgr_is_checkpoint_fs(rec)) {
+        if (!strcmp(rec->fs_type, "f2fs")) {
+            std::string opts(rec->fs_options);
+
+            opts += ",checkpoint=disable";
+            free(rec->fs_options);
+            rec->fs_options = strdup(opts.c_str());
+        } else {
+            LERROR << rec->fs_type << " does not implement checkpoints.";
+        }
+    } else if (fs_mgr_is_checkpoint_blk(rec)) {
+        call_vdc({"checkpoint", "restoreCheckpoint", rec->blk_device});
+
+        android::base::unique_fd fd(
+                TEMP_FAILURE_RETRY(open(rec->blk_device, O_RDONLY | O_CLOEXEC)));
+        if (!fd) {
+            PERROR << "Cannot open device " << rec->blk_device;
+            return false;
+        }
+
+        uint64_t size = get_block_device_size(fd) / 512;
+        if (!size) {
+            PERROR << "Cannot get device size";
+            return false;
+        }
+
+        android::dm::DmTable table;
+        if (!table.AddTarget(
+                    std::make_unique<android::dm::DmTargetBow>(0, size, rec->blk_device))) {
+            LERROR << "Failed to add Bow target";
+            return false;
+        }
+
+        DeviceMapper& dm = DeviceMapper::Instance();
+        if (!dm.CreateDevice("bow", table)) {
+            PERROR << "Failed to create bow device";
+            return false;
+        }
+
+        std::string name;
+        if (!dm.GetDmDevicePathByName("bow", &name)) {
+            PERROR << "Failed to get bow device name";
+            return false;
+        }
+
+        rec->blk_device = strdup(name.c_str());
+    }
+    return true;
+}
+
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
  * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
  */
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
-{
+int fs_mgr_mount_all(fstab* fstab, int mount_mode) {
     int i = 0;
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     int error_count = 0;
     int mret = -1;
     int mount_errno = 0;
     int attempted_idx = -1;
+    int need_checkpoint = -1;
     FsManagerAvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -882,6 +960,18 @@
             }
         }
 
+        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+            if (need_checkpoint == -1 &&
+                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+                need_checkpoint = 0;
+            }
+            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up checkpoint partition, skipping!";
+                continue;
+            }
+        }
+
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
             !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
             LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
@@ -1045,7 +1135,7 @@
     }
 
 #if ALLOW_ADBD_DISABLE_VERITY == 1  // "userdebug" build
-    fs_mgr_overlayfs_mount_all();
+    fs_mgr_overlayfs_mount_all(fstab);
 #endif
 
     if (error_count) {
@@ -1081,9 +1171,8 @@
  * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
  * in turn, and stop on 1st success, or no more match.
  */
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
-                    char *tmp_mount_point)
-{
+static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
+                                  char* tmp_mount_point, int need_checkpoint) {
     int i = 0;
     int mount_errors = 0;
     int first_mount_errno = 0;
@@ -1116,6 +1205,18 @@
             }
         }
 
+        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+            if (need_checkpoint == -1 &&
+                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+                need_checkpoint = 0;
+            }
+            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up checkpoint partition, skipping!";
+                continue;
+            }
+        }
+
         /* First check the filesystem if requested */
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
@@ -1185,6 +1286,15 @@
     return FS_MGR_DOMNT_FAILED;
 }
 
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+}
+
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+                    bool needs_cp) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_cp);
+}
+
 /*
  * mount a tmpfs filesystem at the given point.
  * return 0 on success, non-zero on failure.
@@ -1207,8 +1317,7 @@
 /* This must be called after mount_all, because the mkswap command needs to be
  * available.
  */
-int fs_mgr_swapon_all(struct fstab *fstab)
-{
+int fs_mgr_swapon_all(fstab* fstab) {
     int i = 0;
     int flags = 0;
     int err = 0;
@@ -1235,29 +1344,25 @@
              * on a system (all the memory comes from the same pool) so
              * we can assume the device number is 0.
              */
-            FILE *zram_fp;
-            FILE *zram_mcs_fp;
-
             if (fstab->recs[i].max_comp_streams >= 0) {
-               zram_mcs_fp = fopen(ZRAM_CONF_MCS, "r+");
-              if (zram_mcs_fp == NULL) {
-                LERROR << "Unable to open zram conf comp device "
-                       << ZRAM_CONF_MCS;
-                ret = -1;
-                continue;
-              }
-              fprintf(zram_mcs_fp, "%d\n", fstab->recs[i].max_comp_streams);
-              fclose(zram_mcs_fp);
+                auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{
+                        fopen(ZRAM_CONF_MCS, "re"), fclose};
+                if (zram_mcs_fp == NULL) {
+                    LERROR << "Unable to open zram conf comp device " << ZRAM_CONF_MCS;
+                    ret = -1;
+                    continue;
+                }
+                fprintf(zram_mcs_fp.get(), "%d\n", fstab->recs[i].max_comp_streams);
             }
 
-            zram_fp = fopen(ZRAM_CONF_DEV, "r+");
+            auto zram_fp =
+                    std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, "re+"), fclose};
             if (zram_fp == NULL) {
                 LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
                 ret = -1;
                 continue;
             }
-            fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
-            fclose(zram_fp);
+            fprintf(zram_fp.get(), "%u\n", fstab->recs[i].zram_size);
         }
 
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
@@ -1298,7 +1403,7 @@
     return ret;
 }
 
-struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
+struct fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab) {
     int i;
 
     if (!fstab) {
@@ -1322,7 +1427,7 @@
  *
  * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
  */
-void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
+void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
     struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
     if (key_loc) {
         if (rec) {
@@ -1436,3 +1541,7 @@
 
     return true;
 }
+
+std::string fs_mgr_get_super_partition_name(int /* slot */) {
+    return LP_METADATA_DEFAULT_PARTITION_NAME;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f87a3b1..fc3a05c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -80,37 +80,39 @@
 };
 
 static struct flag_list fs_mgr_flags[] = {
-    {"wait", MF_WAIT},
-    {"check", MF_CHECK},
-    {"encryptable=", MF_CRYPT},
-    {"forceencrypt=", MF_FORCECRYPT},
-    {"fileencryption=", MF_FILEENCRYPTION},
-    {"forcefdeorfbe=", MF_FORCEFDEORFBE},
-    {"keydirectory=", MF_KEYDIRECTORY},
-    {"nonremovable", MF_NONREMOVABLE},
-    {"voldmanaged=", MF_VOLDMANAGED},
-    {"length=", MF_LENGTH},
-    {"recoveryonly", MF_RECOVERYONLY},
-    {"swapprio=", MF_SWAPPRIO},
-    {"zramsize=", MF_ZRAMSIZE},
-    {"max_comp_streams=", MF_MAX_COMP_STREAMS},
-    {"verifyatboot", MF_VERIFYATBOOT},
-    {"verify", MF_VERIFY},
-    {"avb", MF_AVB},
-    {"noemulatedsd", MF_NOEMULATEDSD},
-    {"notrim", MF_NOTRIM},
-    {"formattable", MF_FORMATTABLE},
-    {"slotselect", MF_SLOTSELECT},
-    {"nofail", MF_NOFAIL},
-    {"latemount", MF_LATEMOUNT},
-    {"reservedsize=", MF_RESERVEDSIZE},
-    {"quota", MF_QUOTA},
-    {"eraseblk=", MF_ERASEBLKSIZE},
-    {"logicalblk=", MF_LOGICALBLKSIZE},
-    {"sysfs_path=", MF_SYSFS},
-    {"defaults", 0},
-    {"logical", MF_LOGICAL},
-    {0, 0},
+        {"wait", MF_WAIT},
+        {"check", MF_CHECK},
+        {"encryptable=", MF_CRYPT},
+        {"forceencrypt=", MF_FORCECRYPT},
+        {"fileencryption=", MF_FILEENCRYPTION},
+        {"forcefdeorfbe=", MF_FORCEFDEORFBE},
+        {"keydirectory=", MF_KEYDIRECTORY},
+        {"nonremovable", MF_NONREMOVABLE},
+        {"voldmanaged=", MF_VOLDMANAGED},
+        {"length=", MF_LENGTH},
+        {"recoveryonly", MF_RECOVERYONLY},
+        {"swapprio=", MF_SWAPPRIO},
+        {"zramsize=", MF_ZRAMSIZE},
+        {"max_comp_streams=", MF_MAX_COMP_STREAMS},
+        {"verifyatboot", MF_VERIFYATBOOT},
+        {"verify", MF_VERIFY},
+        {"avb", MF_AVB},
+        {"noemulatedsd", MF_NOEMULATEDSD},
+        {"notrim", MF_NOTRIM},
+        {"formattable", MF_FORMATTABLE},
+        {"slotselect", MF_SLOTSELECT},
+        {"nofail", MF_NOFAIL},
+        {"latemount", MF_LATEMOUNT},
+        {"reservedsize=", MF_RESERVEDSIZE},
+        {"quota", MF_QUOTA},
+        {"eraseblk=", MF_ERASEBLKSIZE},
+        {"logicalblk=", MF_LOGICALBLKSIZE},
+        {"sysfs_path=", MF_SYSFS},
+        {"defaults", 0},
+        {"logical", MF_LOGICAL},
+        {"checkpoint=block", MF_CHECKPOINT_BLK},
+        {"checkpoint=fs", MF_CHECKPOINT_FS},
+        {0, 0},
 };
 
 #define EM_AES_256_XTS  1
@@ -730,21 +732,19 @@
 
 struct fstab *fs_mgr_read_fstab(const char *fstab_path)
 {
-    FILE *fstab_file;
     struct fstab *fstab;
 
-    fstab_file = fopen(fstab_path, "r");
+    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fstab_path, "re"), fclose};
     if (!fstab_file) {
         PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
         return nullptr;
     }
 
-    fstab = fs_mgr_read_fstab_file(fstab_file, !strcmp("/proc/mounts", fstab_path));
+    fstab = fs_mgr_read_fstab_file(fstab_file.get(), !strcmp("/proc/mounts", fstab_path));
     if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
     }
 
-    fclose(fstab_file);
     return fstab;
 }
 
@@ -1004,3 +1004,15 @@
 int fs_mgr_is_logical(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_LOGICAL;
 }
+
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & (MF_CHECKPOINT_FS | MF_CHECKPOINT_BLK);
+}
+
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_CHECKPOINT_FS;
+}
+
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 720dcfd..95326d1 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -49,10 +49,14 @@
 
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab*) {
     return false;
 }
 
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab*) {
+    return {};
+}
+
 bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change) {
     if (change) *change = false;
     return false;
@@ -109,9 +113,9 @@
     struct statvfs vst;
     if (statvfs(mount_point, &vst)) return true;
 
-    static constexpr int percent = 1;  // 1%
+    static constexpr int kPercentThreshold = 1;  // 1%
 
-    return (vst.f_bfree >= (vst.f_blocks * percent / 100));
+    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
 bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
@@ -123,32 +127,32 @@
            !fs_mgr_filesystem_has_space(fsrec->mount_point);
 }
 
-constexpr char upper_name[] = "upper";
-constexpr char work_name[] = "work";
+const auto kUpperName = "upper"s;
+const auto kWorkName = "work"s;
+const auto kOverlayTopDir = "/overlay"s;
 
 std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
     if (!fs_mgr_is_dir(mount_point)) return "";
-    auto dir = kOverlayMountPoint + "/overlay/" + android::base::Basename(mount_point) + "/";
-    auto upper = dir + upper_name;
+    auto dir =
+            kOverlayMountPoint + kOverlayTopDir + "/" + android::base::Basename(mount_point) + "/";
+    auto upper = dir + kUpperName;
     if (!fs_mgr_is_dir(upper)) return "";
-    auto work = dir + work_name;
+    auto work = dir + kWorkName;
     if (!fs_mgr_is_dir(work)) return "";
     if (!fs_mgr_dir_is_writable(work)) return "";
     return dir;
 }
 
-constexpr char lowerdir_option[] = "lowerdir=";
-constexpr char upperdir_option[] = "upperdir=";
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
 
 // default options for mount_point, returns empty string for none available.
 std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
     auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
     if (candidate.empty()) return "";
 
-    auto context = fs_mgr_get_context(mount_point);
-    if (!context.empty()) context = ",rootcontext="s + context;
-    return "override_creds=off,"s + lowerdir_option + mount_point + "," + upperdir_option +
-           candidate + upper_name + ",workdir=" + candidate + work_name + context;
+    return "override_creds=off,"s + kLowerdirOption + mount_point + "," + kUpperdirOption +
+           candidate + kUpperName + ",workdir=" + candidate + kWorkName;
 }
 
 bool fs_mgr_system_root_image(const fstab* fstab) {
@@ -173,9 +177,16 @@
     return "/system";
 }
 
+bool fs_mgr_access(const std::string& path) {
+    auto save_errno = errno;
+    auto ret = access(path.c_str(), F_OK) == 0;
+    errno = save_errno;
+    return ret;
+}
+
 // return true if system supports overlayfs
 bool fs_mgr_wants_overlayfs() {
-    // This will return empty on init first_stage_mount, so speculative
+    // Properties will return empty on init first_stage_mount, so speculative
     // determination, empty (unset) _or_ "1" is true which differs from the
     // official ro.debuggable policy.  ALLOW_ADBD_DISABLE_VERITY == 0 should
     // protect us from false in any case, so this is insurance.
@@ -183,13 +194,41 @@
     if (debuggable != "1") return false;
 
     // Overlayfs available in the kernel, and patched for override_creds?
-    static signed char overlayfs_in_kernel = -1;  // cache for constant condition
-    if (overlayfs_in_kernel == -1) {
-        auto save_errno = errno;
-        overlayfs_in_kernel = !access("/sys/module/overlay/parameters/override_creds", F_OK);
-        errno = save_errno;
+    return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
+}
+
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
+                                                               fs_mgr_free_fstab);
+    if (!fstab) return false;
+    const auto lowerdir = kLowerdirOption + mount_point;
+    for (auto i = 0; i < fstab->num_entries; ++i) {
+        const auto fsrec = &fstab->recs[i];
+        const auto fs_type = fsrec->fs_type;
+        if (!fs_type) continue;
+        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+        auto fsrec_mount_point = fsrec->mount_point;
+        if (!fsrec_mount_point) continue;
+        if (mount_point != fsrec_mount_point) continue;
+        const auto fs_options = fsrec->fs_options;
+        if (!fs_options) continue;
+        const auto options = android::base::Split(fs_options, ",");
+        for (const auto& opt : options) {
+            if (opt == lowerdir) {
+                return true;
+            }
+        }
     }
-    return overlayfs_in_kernel;
+    return false;
+}
+
+bool fs_mgr_overlayfs_verity_enabled(const std::string& basename_mount_point) {
+    auto found = false;
+    fs_mgr_update_verity_state(
+            [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
+                if (mount_point && (basename_mount_point == mount_point)) found = true;
+            });
+    return found;
 }
 
 bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
@@ -215,14 +254,7 @@
 
     if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
 
-    // Verity enabled?
-    const auto basename_mount_point(android::base::Basename(fsrec_mount_point));
-    auto found = false;
-    fs_mgr_update_verity_state(
-            [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
-                if (mount_point && (basename_mount_point == mount_point)) found = true;
-            });
-    return !found;
+    return !fs_mgr_overlayfs_verity_enabled(android::base::Basename(fsrec_mount_point));
 }
 
 bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr) {
@@ -233,7 +265,7 @@
             errno = save_errno;
             return true;
         }
-        PERROR << "overlayfs open " << path;
+        PERROR << "opendir " << path;
         return false;
     }
     dirent* entry;
@@ -251,7 +283,7 @@
                 if (change) *change = true;
             } else {
                 ret = false;
-                PERROR << "overlayfs rmdir " << file;
+                PERROR << "rmdir " << file;
             }
             continue;
         }
@@ -259,39 +291,61 @@
             if (change) *change = true;
         } else {
             ret = false;
-            PERROR << "overlayfs rm " << file;
+            PERROR << "rm " << file;
         }
     }
     return ret;
 }
 
-constexpr char overlayfs_file_context[] = "u:object_r:overlayfs_file:s0";
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
+bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
+    auto ret = true;
+    auto top = dir + kOverlayTopDir;
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        ret = false;
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    }
+    auto save_errno = errno;
+    if (!mkdir(top.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "mkdir " << top;
+    } else {
+        errno = save_errno;
+    }
+    setfscreatecon(nullptr);
+
+    if (overlay) *overlay = std::move(top);
+    return ret;
+}
 
 bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
                                 bool* change) {
     auto ret = true;
-    auto fsrec_mount_point = overlay + android::base::Basename(mount_point) + "/";
+    auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
 
-    if (setfscreatecon(overlayfs_file_context)) {
+    if (setfscreatecon(kOverlayfsFileContext)) {
         ret = false;
-        PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
     }
     auto save_errno = errno;
     if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << fsrec_mount_point;
+        PERROR << "mkdir " << fsrec_mount_point;
     } else {
         errno = save_errno;
     }
 
     save_errno = errno;
-    if (!mkdir((fsrec_mount_point + work_name).c_str(), 0755)) {
+    if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << fsrec_mount_point << work_name;
+        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
     } else {
         errno = save_errno;
     }
@@ -300,15 +354,15 @@
     auto new_context = fs_mgr_get_context(mount_point);
     if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
         ret = false;
-        PERROR << "overlayfs setfscreatecon " << new_context;
+        PERROR << "setfscreatecon " << new_context;
     }
-    auto upper = fsrec_mount_point + upper_name;
+    auto upper = fsrec_mount_point + kUpperName;
     save_errno = errno;
     if (!mkdir(upper.c_str(), 0755)) {
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << upper;
+        PERROR << "mkdir " << upper;
     } else {
         errno = save_errno;
     }
@@ -317,6 +371,49 @@
     return ret;
 }
 
+bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
+                                   bool* change) {
+    const auto top = overlay + kOverlayTopDir;
+
+    if (!fs_mgr_access(top)) return false;
+
+    auto cleanup_all = mount_point.empty();
+    const auto oldpath = top + (cleanup_all ? "" : ("/"s + mount_point));
+    const auto newpath = oldpath + ".teardown";
+    auto ret = fs_mgr_rm_all(newpath);
+    auto save_errno = errno;
+    if (!rename(oldpath.c_str(), newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "mv " << oldpath << " " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    ret &= fs_mgr_rm_all(newpath, change);
+    save_errno = errno;
+    if (!rmdir(newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "rmdir " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    if (!cleanup_all) {
+        save_errno = errno;
+        if (!rmdir(top.c_str())) {
+            if (change) *change = true;
+        } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
+            ret = false;
+            PERROR << "rmdir " << top;
+        } else {
+            errno = save_errno;
+        }
+    }
+    return ret;
+}
+
 bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
     auto options = fs_mgr_get_overlayfs_options(mount_point);
     if (options.empty()) return false;
@@ -325,7 +422,7 @@
     auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
     const auto opt_list = android::base::Split(options, ",");
     for (const auto opt : opt_list) {
-        if (android::base::StartsWith(opt, upperdir_option)) {
+        if (android::base::StartsWith(opt, kUpperdirOption)) {
             report = report + "," + opt;
             break;
         }
@@ -343,31 +440,6 @@
     }
 }
 
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
-            fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab);
-    if (!fstab) return false;
-    const auto lowerdir = std::string(lowerdir_option) + mount_point;
-    for (auto i = 0; i < fstab->num_entries; ++i) {
-        const auto fsrec = &fstab->recs[i];
-        const auto fs_type = fsrec->fs_type;
-        if (!fs_type) continue;
-        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
-        auto fsrec_mount_point = fsrec->mount_point;
-        if (!fsrec_mount_point) continue;
-        if (mount_point != fsrec_mount_point) continue;
-        const auto fs_options = fsrec->fs_options;
-        if (!fs_options) continue;
-        const auto options = android::base::Split(fs_options, ",");
-        for (const auto opt : options) {
-            if (opt == lowerdir) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
 std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
                                                const char* mount_point = nullptr) {
     std::vector<std::string> mounts;
@@ -393,27 +465,39 @@
         }
         if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
     }
+    // if not itemized /system or /, system as root, fake up
+    // fs_mgr_wants_overlayfs evaluation of /system as candidate.
+
+    if ((std::find(mounts.begin(), mounts.end(), "/system") == mounts.end()) &&
+        !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/") &&
+        !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system") &&
+        (!mount_point || ("/system"s == mount_point)) &&
+        !fs_mgr_overlayfs_verity_enabled("system")) {
+        mounts.emplace_back("/system");
+    }
     return mounts;
 }
 
 }  // namespace
 
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
     auto ret = false;
 
     if (!fs_mgr_wants_overlayfs()) return ret;
 
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                      fs_mgr_free_fstab);
     if (!fstab) return ret;
 
-    for (const auto& mount_point : fs_mgr_candidate_list(fstab.get())) {
+    for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
         if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
         if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
     }
     return ret;
 }
 
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab*) {
+    return {};
+}
+
 // Returns false if setup not permitted, errno set to last error.
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
@@ -426,29 +510,19 @@
     if (!fs_mgr_wants_overlayfs()) return ret;
     if (!fs_mgr_boot_completed()) {
         errno = EBUSY;
-        PERROR << "overlayfs setup";
+        PERROR << "setup";
         return ret;
     }
 
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                      fs_mgr_free_fstab);
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
     if (fstab && !fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
     auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(fstab.get(), mount_point));
     if (fstab && mounts.empty()) return ret;
 
-    if (setfscreatecon(overlayfs_file_context)) {
-        PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
-    }
-    auto overlay = kOverlayMountPoint + "/overlay/";
-    auto save_errno = errno;
-    if (!mkdir(overlay.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        PERROR << "overlayfs mkdir " << overlay;
-    } else {
-        errno = save_errno;
-    }
-    setfscreatecon(nullptr);
+    std::string overlay;
+    ret |= fs_mgr_overlayfs_setup_dir(kOverlayMountPoint, &overlay, change);
+
     if (!fstab && mount_point && fs_mgr_overlayfs_setup_one(overlay, mount_point, change)) {
         ret = true;
     }
@@ -462,45 +536,11 @@
 // 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(std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)>(
+    mount_point = fs_mgr_mount_point(std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>(
                                              fs_mgr_read_fstab_default(), fs_mgr_free_fstab)
                                              .get(),
                                      mount_point);
-    auto ret = true;
-    const auto overlay = kOverlayMountPoint + "/overlay";
-    const auto oldpath = overlay + (mount_point ?: "");
-    const auto newpath = oldpath + ".teardown";
-    ret &= fs_mgr_rm_all(newpath);
-    auto save_errno = errno;
-    if (!rename(oldpath.c_str(), newpath.c_str())) {
-        if (change) *change = true;
-    } else if (errno != ENOENT) {
-        ret = false;
-        PERROR << "overlayfs mv " << oldpath << " " << newpath;
-    } else {
-        errno = save_errno;
-    }
-    ret &= fs_mgr_rm_all(newpath, change);
-    save_errno = errno;
-    if (!rmdir(newpath.c_str())) {
-        if (change) *change = true;
-    } else if (errno != ENOENT) {
-        ret = false;
-        PERROR << "overlayfs rmdir " << newpath;
-    } else {
-        errno = save_errno;
-    }
-    if (mount_point) {
-        save_errno = errno;
-        if (!rmdir(overlay.c_str())) {
-            if (change) *change = true;
-        } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
-            ret = false;
-            PERROR << "overlayfs rmdir " << overlay;
-        } else {
-            errno = save_errno;
-        }
-    }
+    auto ret = fs_mgr_overlayfs_teardown_one(kOverlayMountPoint, mount_point ?: "", change);
     if (!fs_mgr_wants_overlayfs()) {
         // After obligatory teardown to make sure everything is clean, but if
         // we didn't want overlayfs in the the first place, we do not want to
@@ -511,7 +551,7 @@
     // caller that there may still be more to do.
     if (!fs_mgr_boot_completed()) {
         errno = EBUSY;
-        PERROR << "overlayfs teardown";
+        PERROR << "teardown";
         ret = false;
     }
     return ret;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index ebc4a0f..506e81d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -113,6 +113,8 @@
 #define MF_KEYDIRECTORY    0X4000000
 #define MF_SYSFS           0X8000000
 #define MF_LOGICAL        0x10000000
+#define MF_CHECKPOINT_BLK 0x20000000
+#define MF_CHECKPOINT_FS  0x40000000
 // clang-format on
 
 #define DM_BUF_SIZE 4096
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fb4ebb..2727a6d 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -89,24 +89,21 @@
 {
     uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
 
-    FILE* f = fopen(path, "r");
+    auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
     if (!f) {
         LERROR << "Can't open " << path;
-        return NULL;
+        return nullptr;
     }
 
-    if (!fread(key_data, sizeof(key_data), 1, f)) {
+    if (!fread(key_data, sizeof(key_data), 1, f.get())) {
         LERROR << "Could not read key!";
-        fclose(f);
-        return NULL;
+        return nullptr;
     }
 
-    fclose(f);
-
-    RSA* key = NULL;
+    RSA* key = nullptr;
     if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
         LERROR << "Could not parse key!";
-        return NULL;
+        return nullptr;
     }
 
     return key;
@@ -368,7 +365,6 @@
 static int metadata_find(const char *fname, const char *stag,
         unsigned int slength, off64_t *offset)
 {
-    FILE *fp = NULL;
     char tag[METADATA_TAG_MAX_LENGTH + 1];
     int rc = -1;
     int n;
@@ -380,75 +376,64 @@
         return -1;
     }
 
-    fp = fopen(fname, "r+");
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
 
     if (!fp) {
         PERROR << "Failed to open " << fname;
-        goto out;
+        return -1;
     }
 
     /* check magic */
-    if (fseek(fp, start, SEEK_SET) < 0 ||
-        fread(&magic, sizeof(magic), 1, fp) != 1) {
+    if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
         PERROR << "Failed to read magic from " << fname;
-        goto out;
+        return -1;
     }
 
     if (magic != METADATA_MAGIC) {
         magic = METADATA_MAGIC;
 
-        if (fseek(fp, start, SEEK_SET) < 0 ||
-            fwrite(&magic, sizeof(magic), 1, fp) != 1) {
+        if (fseek(fp.get(), start, SEEK_SET) < 0 ||
+            fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
             PERROR << "Failed to write magic to " << fname;
-            goto out;
+            return -1;
         }
 
-        rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
+        rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
         if (rc < 0) {
             PERROR << "Failed to add metadata to " << fname;
         }
 
-        goto out;
+        return rc;
     }
 
     start += sizeof(magic);
 
     while (1) {
-        n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
-                tag, &length);
+        n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
 
         if (n == 2 && strcmp(tag, METADATA_EOD)) {
             /* found a tag */
-            start = ftell(fp);
+            start = ftell(fp.get());
 
             if (!strcmp(tag, stag) && length == slength) {
                 *offset = start;
-                rc = 0;
-                goto out;
+                return 0;
             }
 
             start += length;
 
-            if (fseek(fp, length, SEEK_CUR) < 0) {
+            if (fseek(fp.get(), length, SEEK_CUR) < 0) {
                 PERROR << "Failed to seek " << fname;
-                goto out;
+                return -1;
             }
         } else {
-            rc = metadata_add(fp, start, stag, slength, offset);
+            rc = metadata_add(fp.get(), start, stag, slength, offset);
             if (rc < 0) {
                 PERROR << "Failed to write metadata to " << fname;
             }
-            goto out;
+            return rc;
         }
-   }
-
-out:
-    if (fp) {
-        fflush(fp);
-        fclose(fp);
     }
-
-    return rc;
 }
 
 static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 1049fb6..a4544b2 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -23,6 +23,7 @@
 #include <linux/dm-ioctl.h>
 
 #include <functional>
+#include <string>
 
 #include <fstab/fstab.h>
 
@@ -70,6 +71,8 @@
 
 int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
+int fs_mgr_do_mount(struct fstab* fstab, const char* n_name, char* n_blk_device,
+                    char* tmp_mount_point, bool need_cp);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
@@ -87,4 +90,9 @@
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
 int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
 
+// Return the name of the super partition if it exists. If a slot number is
+// specified, the super partition for the corresponding metadata slot will be
+// returned. Otherwise, it will use the current slot.
+std::string fs_mgr_get_super_partition_name(int slot = -1);
+
 #endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index ceb45de..3274e0e 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -19,8 +19,10 @@
 #include <fstab/fstab.h>
 
 #include <string>
+#include <vector>
 
-bool fs_mgr_overlayfs_mount_all();
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index b1ee328..bb40511 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -86,6 +86,9 @@
 int fs_mgr_is_latemount(const struct fstab_rec* fstab);
 int fs_mgr_is_quota(const struct fstab_rec* fstab);
 int fs_mgr_is_logical(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab);
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index cc61917..70823c6 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -59,7 +59,8 @@
         : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
         valid_ = dm_.CreateDevice(name, table);
     }
-    TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+    TempDevice(TempDevice&& other) noexcept
+        : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
         other.valid_ = false;
     }
     ~TempDevice() {
@@ -103,7 +104,7 @@
     TempDevice(const TempDevice&) = delete;
     TempDevice& operator=(const TempDevice&) = delete;
 
-    TempDevice& operator=(TempDevice&& other) {
+    TempDevice& operator=(TempDevice&& other) noexcept {
         name_ = other.name_;
         valid_ = other.valid_;
         other.valid_ = false;
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index aab89e5..175b0f0 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -156,6 +156,20 @@
     std::string target_string_;
 };
 
+// dm-bow is the backup on write target that can provide checkpoint capability
+// for file systems that do not support checkpoints natively
+class DmTargetBow final : public DmTarget {
+  public:
+    DmTargetBow(uint64_t start, uint64_t length, const std::string& target_string)
+        : DmTarget(start, length), target_string_(target_string) {}
+
+    std::string name() const override { return "bow"; }
+    std::string GetParameterString() const override { return target_string_; }
+
+  private:
+    std::string target_string_;
+};
+
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 89282db..69dc065 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -35,7 +35,6 @@
         "libcrypto",
         "libcrypto_utils",
         "libsparse",
-        "libext2_uuid",
         "libext4_utils",
         "libz",
     ],
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 018c280..97b15bd 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -25,7 +25,6 @@
 #include <algorithm>
 
 #include <android-base/unique_fd.h>
-#include <uuid/uuid.h>
 
 #include "liblp/liblp.h"
 #include "reader.h"
@@ -79,8 +78,8 @@
     out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
 }
 
-Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
-    : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+    : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
 
 void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
     size_ += extent->num_sectors() * LP_SECTOR_SIZE;
@@ -128,6 +127,17 @@
     DCHECK(size_ == aligned_size);
 }
 
+uint64_t Partition::BytesOnDisk() const {
+    uint64_t sectors = 0;
+    for (const auto& extent : extents_) {
+        if (!extent->AsLinearExtent()) {
+            continue;
+        }
+        sectors += extent->num_sectors();
+    }
+    return sectors * LP_SECTOR_SIZE;
+}
+
 std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
                                                       uint32_t slot_number) {
     std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
@@ -175,14 +185,23 @@
     header_.header_size = sizeof(header_);
     header_.partitions.entry_size = sizeof(LpMetadataPartition);
     header_.extents.entry_size = sizeof(LpMetadataExtent);
+    header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
 }
 
 bool MetadataBuilder::Init(const LpMetadata& metadata) {
     geometry_ = metadata.geometry;
 
+    for (const auto& group : metadata.groups) {
+        std::string group_name = GetPartitionGroupName(group);
+        if (!AddGroup(group_name, group.maximum_size)) {
+            return false;
+        }
+    }
+
     for (const auto& partition : metadata.partitions) {
-        Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
-                                          partition.attributes);
+        std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
+        Partition* builder =
+                AddPartition(GetPartitionName(partition), group_name, partition.attributes);
         if (!builder) {
             return false;
         }
@@ -292,10 +311,27 @@
     geometry_.alignment_offset = device_info_.alignment_offset;
     geometry_.block_device_size = device_info_.size;
     geometry_.logical_block_size = device_info.logical_block_size;
+
+    if (!AddGroup("default", 0)) {
+        return false;
+    }
     return true;
 }
 
-Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
+    if (FindGroup(group_name)) {
+        LERROR << "Group already exists: " << group_name;
+        return false;
+    }
+    groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));
+    return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
+    return AddPartition(name, "default", attributes);
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
                                          uint32_t attributes) {
     if (name.empty()) {
         LERROR << "Partition must have a non-empty name.";
@@ -305,7 +341,11 @@
         LERROR << "Attempting to create duplication partition with name: " << name;
         return nullptr;
     }
-    partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+    if (!FindGroup(group_name)) {
+        LERROR << "Could not find partition group: " << group_name;
+        return nullptr;
+    }
+    partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
     return partitions_.back().get();
 }
 
@@ -318,6 +358,26 @@
     return nullptr;
 }
 
+PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) const {
+    for (const auto& group : groups_) {
+        if (group->name() == group_name) {
+            return group.get();
+        }
+    }
+    return nullptr;
+}
+
+uint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {
+    uint64_t total = 0;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() != group->name()) {
+            continue;
+        }
+        total += partition->BytesOnDisk();
+    }
+    return total;
+}
+
 void MetadataBuilder::RemovePartition(const std::string& name) {
     for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
         if ((*iter)->name() == name) {
@@ -328,8 +388,23 @@
 }
 
 bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
-    // Figure out how much we need to allocate.
+    PartitionGroup* group = FindGroup(partition->group_name());
+    CHECK(group);
+
+    // Figure out how much we need to allocate, and whether our group has
+    // enough space remaining.
     uint64_t space_needed = aligned_size - partition->size();
+    if (group->maximum_size() > 0) {
+        uint64_t group_size = TotalSizeOfGroup(group);
+        if (group_size >= group->maximum_size() ||
+            group->maximum_size() - group_size < space_needed) {
+            LERROR << "Partition " << partition->name() << " is part of group " << group->name()
+                   << " which does not have enough space free (" << space_needed << "requested, "
+                   << group_size << " used out of " << group->maximum_size();
+            return false;
+        }
+    }
+
     uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
     DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
 
@@ -441,6 +516,20 @@
     metadata->header = header_;
     metadata->geometry = geometry_;
 
+    std::map<std::string, size_t> group_indices;
+    for (const auto& group : groups_) {
+        LpMetadataPartitionGroup out = {};
+
+        if (group->name().size() > sizeof(out.name)) {
+            LERROR << "Partition group name is too long: " << group->name();
+            return nullptr;
+        }
+        strncpy(out.name, group->name().c_str(), sizeof(out.name));
+        out.maximum_size = group->maximum_size();
+
+        metadata->groups.push_back(out);
+    }
+
     // Flatten the partition and extent structures into an LpMetadata, which
     // makes it very easy to validate, serialize, or pass on to device-mapper.
     for (const auto& partition : partitions_) {
@@ -457,12 +546,6 @@
         }
 
         strncpy(part.name, partition->name().c_str(), sizeof(part.name));
-        if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
-            LERROR << "Could not parse guid " << partition->guid() << " for partition "
-                   << partition->name();
-            return nullptr;
-        }
-
         part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
         part.num_extents = static_cast<uint32_t>(partition->extents().size());
         part.attributes = partition->attributes();
@@ -475,6 +558,7 @@
 
     metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
     metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+    metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
     return metadata;
 }
 
@@ -482,6 +566,14 @@
     return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
 }
 
+uint64_t MetadataBuilder::UsedSpace() const {
+    uint64_t size = 0;
+    for (const auto& partition : partitions_) {
+        size += partition->size();
+    }
+    return size;
+}
+
 uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
@@ -522,8 +614,10 @@
         ShrinkPartition(partition, aligned_size);
     }
 
-    LINFO << "Partition " << partition->name() << " will resize from " << old_size << " bytes to "
-          << aligned_size << " bytes";
+    if (partition->size() != old_size) {
+        LINFO << "Partition " << partition->name() << " will resize from " << old_size
+              << " bytes to " << aligned_size << " bytes";
+    }
     return true;
 }
 
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index da9c8f3..c916b44 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -22,16 +22,12 @@
 using namespace std;
 using namespace android::fs_mgr;
 
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
-static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
-
 TEST(liblp, BuildBasic) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(partition, nullptr);
     EXPECT_EQ(partition->name(), "system");
-    EXPECT_EQ(partition->guid(), TEST_GUID);
     EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(partition->size(), 0);
     EXPECT_EQ(builder->FindPartition("system"), partition);
@@ -43,7 +39,7 @@
 TEST(liblp, ResizePartition) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
     EXPECT_EQ(system->size(), 65536);
@@ -94,7 +90,7 @@
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
     // Test that we align up to one sector.
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 10000), true);
     EXPECT_EQ(system->size(), 12288);
@@ -171,9 +167,9 @@
     BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
 
-    Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+    Partition* a = builder->AddPartition("a", 0);
     ASSERT_NE(a, nullptr);
-    Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+    Partition* b = builder->AddPartition("b", 0);
     ASSERT_NE(b, nullptr);
 
     // Add a bunch of small extents to each, interleaving.
@@ -202,21 +198,35 @@
 }
 
 TEST(liblp, UseAllDiskSpace) {
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-    EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+    static constexpr uint64_t total = 1024 * 1024;
+    static constexpr uint64_t metadata = 1024;
+    static constexpr uint64_t slots = 2;
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(total, metadata, slots);
+    // We reserve a geometry block (4KB) plus space for each copy of the
+    // maximum size of a metadata blob. Then, we double that space since
+    // we store a backup copy of everything.
+    static constexpr uint64_t geometry = 4 * 1024;
+    static constexpr uint64_t allocatable = total - (metadata * slots + geometry) * 2;
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), 0);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
-    EXPECT_EQ(builder->ResizePartition(system, 1036288), true);
-    EXPECT_EQ(system->size(), 1036288);
-    EXPECT_EQ(builder->ResizePartition(system, 1036289), false);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable), true);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable + 1), false);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
 }
 
 TEST(liblp, BuildComplex) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -249,15 +259,15 @@
 TEST(liblp, AddInvalidPartition) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(partition, nullptr);
 
     // Duplicate name.
-    partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(partition, nullptr);
 
     // Empty name.
-    partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    partition = builder->AddPartition("", LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(partition, nullptr);
 }
 
@@ -268,8 +278,8 @@
     unique_ptr<MetadataBuilder> builder =
             MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -308,7 +318,6 @@
     for (const auto& partition : exported->partitions) {
         Partition* original = builder->FindPartition(GetPartitionName(partition));
         ASSERT_NE(original, nullptr);
-        EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
         for (size_t i = 0; i < partition.num_extents; i++) {
             const auto& extent = exported->extents[partition.first_extent_index + i];
             LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
@@ -323,8 +332,8 @@
 TEST(liblp, BuilderImport) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -343,11 +352,9 @@
 
     EXPECT_EQ(system->size(), 98304);
     ASSERT_EQ(system->extents().size(), 2);
-    EXPECT_EQ(system->guid(), TEST_GUID);
     EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(vendor->size(), 32768);
     ASSERT_EQ(vendor->extents().size(), 1);
-    EXPECT_EQ(vendor->guid(), TEST_GUID2);
     EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
 
     LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
@@ -364,17 +371,7 @@
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
     std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
-    Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    EXPECT_NE(system, nullptr);
-
-    unique_ptr<LpMetadata> exported = builder->Export();
-    EXPECT_EQ(exported, nullptr);
-}
-
-TEST(liblp, ExportInvalidGuid) {
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-
-    Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition(name + name, LP_PARTITION_ATTR_READONLY);
     EXPECT_NE(system, nullptr);
 
     unique_ptr<LpMetadata> exported = builder->Export();
@@ -469,7 +466,7 @@
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
-    Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
+    Partition* partition = builder->AddPartition("system", 0);
     ASSERT_NE(partition, nullptr);
     ASSERT_TRUE(builder->ResizePartition(partition, 512));
     EXPECT_EQ(partition->size(), 4096);
@@ -481,3 +478,28 @@
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
     ASSERT_EQ(builder, nullptr);
 }
+
+TEST(liblp, HasDefaultGroup) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    EXPECT_FALSE(builder->AddGroup("default", 0));
+}
+
+TEST(liblp, GroupSizeLimits) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("google", 16384));
+
+    Partition* partition = builder->AddPartition("system", "google", 0);
+    ASSERT_NE(partition, nullptr);
+    EXPECT_TRUE(builder->ResizePartition(partition, 8192));
+    EXPECT_EQ(partition->size(), 8192);
+    EXPECT_TRUE(builder->ResizePartition(partition, 16384));
+    EXPECT_EQ(partition->size(), 16384);
+    EXPECT_FALSE(builder->ResizePartition(partition, 32768));
+    EXPECT_EQ(partition->size(), 16384);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 38842a4..a6044d0 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -95,11 +95,24 @@
     void AddTo(LpMetadata* out) const override;
 };
 
+class PartitionGroup final {
+  public:
+    explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
+        : name_(name), maximum_size_(maximum_size) {}
+
+    const std::string& name() const { return name_; }
+    uint64_t maximum_size() const { return maximum_size_; }
+
+  private:
+    std::string name_;
+    uint64_t maximum_size_;
+};
+
 class Partition final {
     friend class MetadataBuilder;
 
   public:
-    Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+    Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
 
     // Add a raw extent.
     void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -107,9 +120,13 @@
     // Remove all extents from this partition.
     void RemoveExtents();
 
+    // Compute the size used by linear extents. This is the same as size(),
+    // but does not factor in extents which do not take up space.
+    uint64_t BytesOnDisk() const;
+
     const std::string& name() const { return name_; }
+    const std::string& group_name() const { return group_name_; }
     uint32_t attributes() const { return attributes_; }
-    const std::string& guid() const { return guid_; }
     const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
     uint64_t size() const { return size_; }
 
@@ -117,7 +134,7 @@
     void ShrinkTo(uint64_t aligned_size);
 
     std::string name_;
-    std::string guid_;
+    std::string group_name_;
     std::vector<std::unique_ptr<Extent>> extents_;
     uint32_t attributes_;
     uint64_t size_;
@@ -156,13 +173,25 @@
         return New(device_info, metadata_max_size, metadata_slot_count);
     }
 
+    // Define a new partition group. By default there is one group called
+    // "default", with an unrestricted size. A non-zero size will restrict the
+    // total space used by all partitions in the group.
+    //
+    // This can fail and return false if the group already exists.
+    bool AddGroup(const std::string& group_name, uint64_t maximum_size);
+
     // Export metadata so it can be serialized to an image, to disk, or mounted
     // via device-mapper.
     std::unique_ptr<LpMetadata> Export();
 
     // Add a partition, returning a handle so it can be sized as needed. If a
     // partition with the given name already exists, nullptr is returned.
-    Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+    Partition* AddPartition(const std::string& name, const std::string& group_name,
+                            uint32_t attributes);
+
+    // Same as AddPartition above, but uses the default partition group which
+    // has no size restrictions.
+    Partition* AddPartition(const std::string& name, uint32_t attributes);
 
     // Delete a partition by name if it exists.
     void RemovePartition(const std::string& name);
@@ -184,6 +213,7 @@
 
     // Amount of space that can be allocated to logical partitions.
     uint64_t AllocatableSpace() const;
+    uint64_t UsedSpace() const;
 
     // Merge new block device information into previous values. Alignment values
     // are only overwritten if the new values are non-zero.
@@ -201,10 +231,13 @@
     bool GrowPartition(Partition* partition, uint64_t aligned_size);
     void ShrinkPartition(Partition* partition, uint64_t aligned_size);
     uint64_t AlignSector(uint64_t sector);
+    PartitionGroup* FindGroup(const std::string& group_name) const;
+    uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
 
     LpMetadataGeometry geometry_;
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
+    std::vector<std::unique_ptr<PartitionGroup>> groups_;
     BlockDeviceInfo device_info_;
 };
 
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 6da24f6..5f95dca 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -36,6 +36,7 @@
     LpMetadataHeader header;
     std::vector<LpMetadataPartition> partitions;
     std::vector<LpMetadataExtent> extents;
+    std::vector<LpMetadataPartitionGroup> groups;
 };
 
 // Place an initial partition table on the device. This will overwrite the
@@ -67,7 +68,7 @@
 
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
-std::string GetPartitionGuid(const LpMetadataPartition& partition);
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
 
 // Helper to return a slot number for a slot suffix.
 uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 52c80f7..7d1a2a9 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
 #define LP_METADATA_HEADER_MAGIC 0x414C5030
 
 /* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MAJOR_VERSION 3
 #define LP_METADATA_MINOR_VERSION 0
 
 /* Attributes for the LpMetadataPartition::attributes field.
@@ -67,7 +67,7 @@
  *     | Geometry Backup    |
  *     +--------------------+
  */
-#define LP_METADATA_PARTITION_NAME "super"
+#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
 
 /* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
 #define LP_SECTOR_SIZE 512
@@ -216,6 +216,8 @@
     LpMetadataTableDescriptor partitions;
     /* 92: Extent table descriptor. */
     LpMetadataTableDescriptor extents;
+    /* 104: Updateable group descriptor. */
+    LpMetadataTableDescriptor groups;
 } __attribute__((packed)) LpMetadataHeader;
 
 /* This struct defines a logical partition entry, similar to what would be
@@ -230,21 +232,21 @@
      */
     char name[36];
 
-    /* 36: Globally unique identifier (GUID) of this partition. */
-    uint8_t guid[16];
-
-    /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+    /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
     uint32_t attributes;
 
-    /* 56: Index of the first extent owned by this partition. The extent will
+    /* 40: Index of the first extent owned by this partition. The extent will
      * start at logical sector 0. Gaps between extents are not allowed.
      */
     uint32_t first_extent_index;
 
-    /* 60: Number of extents in the partition. Every partition must have at
+    /* 44: Number of extents in the partition. Every partition must have at
      * least one extent.
      */
     uint32_t num_extents;
+
+    /* 48: Group this partition belongs to. */
+    uint32_t group_index;
 } __attribute__((packed)) LpMetadataPartition;
 
 /* This extent is a dm-linear target, and the index is an index into the
@@ -271,6 +273,19 @@
     uint64_t target_data;
 } __attribute__((packed)) LpMetadataExtent;
 
+/* This struct defines an entry in the groups table. Each group has a maximum
+ * size, and partitions in a group must not exceed that size. There is always
+ * a "default" group of unlimited size, which is used when not using update
+ * groups or when using overlayfs or fastbootd.
+ */
+typedef struct LpMetadataPartitionGroup {
+    /*  0: Name of this group. Any unused characters must be 0. */
+    char name[36];
+
+    /* 36: Maximum size in bytes. If 0, the group has no maximum size. */
+    uint64_t maximum_size;
+} LpMetadataPartitionGroup;
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 329a901..01de3ac 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -37,8 +37,6 @@
 static const size_t kDiskSize = 131072;
 static const size_t kMetadataSize = 512;
 static const size_t kMetadataSlots = 2;
-static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
 
 // Helper function for creating an in-memory file descriptor. This lets us
 // simulate read/writing logical partition metadata as if we had a block device
@@ -81,7 +79,7 @@
 }
 
 static bool AddDefaultPartitions(MetadataBuilder* builder) {
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
     if (!system) {
         return false;
     }
@@ -117,6 +115,9 @@
     uint64_t size;
     ASSERT_TRUE(GetDescriptorSize(fd, &size));
     ASSERT_EQ(size, kDiskSize);
+
+    // Verify that we can't read unwritten metadata.
+    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
 }
 
 // Flashing metadata should not work if the metadata was created for a larger
@@ -168,7 +169,6 @@
     // Check partition tables.
     ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
     EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
-    EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
     EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
     EXPECT_EQ(exported->partitions[0].first_extent_index,
               imported->partitions[0].first_extent_index);
@@ -191,9 +191,6 @@
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
 
-    // Verify that we can't read unwritten metadata.
-    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
-
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
     ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
@@ -331,21 +328,18 @@
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
 
-    // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
-    size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
-    EXPECT_LT(max_partitions, 10);
+    // Compute the maximum number of partitions we can fit in 512 bytes of
+    // metadata. By default there is the header, and one partition group.
+    static const size_t kMaxPartitionTableSize =
+            kMetadataSize - sizeof(LpMetadataHeader) - sizeof(LpMetadataPartitionGroup);
+    size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
 
     // Add this number of partitions.
     Partition* partition = nullptr;
     for (size_t i = 0; i < max_partitions; i++) {
-        std::string guid = std::string(TEST_GUID) + to_string(i);
-        partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+        partition = builder->AddPartition(to_string(i), LP_PARTITION_ATTR_NONE);
         ASSERT_NE(partition, nullptr);
     }
-    ASSERT_NE(partition, nullptr);
-    // Add one extent to any partition to fill up more space - we're at 508
-    // bytes after this, out of 512.
-    ASSERT_TRUE(builder->ResizePartition(partition, 1024));
 
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -357,7 +351,7 @@
     ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
 
     // Check that adding one more partition overflows the metadata allotment.
-    partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    partition = builder->AddPartition("final", LP_PARTITION_ATTR_NONE);
     EXPECT_NE(partition, nullptr);
 
     exported = builder->Export();
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 190c650..005d493 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -188,7 +188,8 @@
         return false;
     }
     if (!ValidateTableBounds(header, header.partitions) ||
-        !ValidateTableBounds(header, header.extents)) {
+        !ValidateTableBounds(header, header.extents) ||
+        !ValidateTableBounds(header, header.groups)) {
         LERROR << "Logical partition metadata has invalid table bounds.";
         return false;
     }
@@ -202,6 +203,10 @@
         LERROR << "Logical partition metadata has invalid extent table entry size.";
         return false;
     }
+    if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
+        LERROR << "Logical partition metadata has invalid group table entry size.";
+        return false;
+    }
     return true;
 }
 
@@ -257,6 +262,10 @@
             LERROR << "Logical partition has invalid extent list.";
             return nullptr;
         }
+        if (partition.group_index >= header.groups.num_entries) {
+            LERROR << "Logical partition has invalid group index.";
+            return nullptr;
+        }
 
         metadata->partitions.push_back(partition);
     }
@@ -269,6 +278,16 @@
 
         metadata->extents.push_back(extent);
     }
+
+    cursor = buffer.get() + header.groups.offset;
+    for (size_t i = 0; i < header.groups.num_entries; i++) {
+        LpMetadataPartitionGroup group = {};
+        memcpy(&group, cursor, sizeof(group));
+        cursor += header.groups.entry_size;
+
+        metadata->groups.push_back(group);
+    }
+
     return metadata;
 }
 
@@ -345,5 +364,9 @@
     return NameFromFixedArray(partition.name, sizeof(partition.name));
 }
 
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
+    return NameFromFixedArray(group.name, sizeof(group.name));
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index a590037..b08f96c 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -23,7 +23,6 @@
 #include <android-base/file.h>
 #include <ext4_utils/ext4_utils.h>
 #include <openssl/sha.h>
-#include <uuid/uuid.h>
 
 #include "utility.h"
 
@@ -80,15 +79,6 @@
     SHA256_Final(out, &c);
 }
 
-std::string GetPartitionGuid(const LpMetadataPartition& partition) {
-    // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
-    // macro to assist with buffer sizing.
-    static const size_t kGuidLen = 36;
-    char buffer[kGuidLen + 1];
-    uuid_unparse_upper(partition.guid, buffer);
-    return buffer;
-}
-
 uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
     if (suffix.empty()) {
         return 0;
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index ad84b22..2415629 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -56,14 +56,17 @@
                            metadata.partitions.size() * sizeof(LpMetadataPartition));
     std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
                         metadata.extents.size() * sizeof(LpMetadataExtent));
+    std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
+                       metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
 
     // Compute positions of tables.
     header.partitions.offset = 0;
     header.extents.offset = header.partitions.offset + partitions.size();
-    header.tables_size = header.extents.offset + extents.size();
+    header.groups.offset = header.extents.offset + extents.size();
+    header.tables_size = header.groups.offset + groups.size();
 
     // Compute payload checksum.
-    std::string tables = partitions + extents;
+    std::string tables = partitions + extents + groups;
     SHA256(tables.data(), tables.size(), header.tables_checksum);
 
     // Compute header checksum.
@@ -94,7 +97,8 @@
     }
     // Make sure we're writing within the space reserved.
     if (blob->size() > geometry.metadata_max_size) {
-        LERROR << "Logical partition metadata is too large.";
+        LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
+               << geometry.metadata_max_size;
         return false;
     }
 
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 8b1c55a..db01c1e 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -139,7 +139,7 @@
     auto fstab = fs_mgr_read_fstab("/proc/mounts");
     ASSERT_NE(fstab, nullptr);
 
-    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "r"),
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "re"),
                                                            endmntent);
     ASSERT_NE(mounts, nullptr);
 
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 879ba21..f78093b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -42,6 +42,7 @@
 using DmTargetLinear = ::android::dm::DmTargetLinear;
 using DmTargetZero = ::android::dm::DmTargetZero;
 using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
+using DmTargetBow = ::android::dm::DmTargetBow;
 using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
 
@@ -108,6 +109,13 @@
             std::string block_device = NextArg();
             return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
                                                            block_device);
+        } else if (target_type == "bow") {
+            if (!HasArgs(1)) {
+                std::cerr << "Expected \"bow\" <block_device>" << std::endl;
+                return nullptr;
+            }
+            std::string block_device = NextArg();
+            return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
         } else {
             std::cerr << "Unrecognized target type: " << target_type << std::endl;
             return nullptr;
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 9096f79..80bf84a 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -97,6 +97,7 @@
     android.hardware.health@2.0 \
     android.hardware.health@1.0 \
     android.hardware.health@1.0-convert \
+    libbinderthreadstate \
     libhidltransport \
     libhidlbase \
     libhwbinder_noltopgo \
diff --git a/init/README.md b/init/README.md
index b45da21..02a65d5 100644
--- a/init/README.md
+++ b/init/README.md
@@ -262,6 +262,13 @@
 > Scheduling priority of the service process. This value has to be in range
   -20 to 19. Default priority is 0. Priority is set via setpriority().
 
+`restart_period <seconds>`
+> If a non-oneshot service exits, it will be restarted at its start time plus
+  this period. It defaults to 5s to rate limit crashing services.
+  This can be increased for services that are meant to run periodically. For
+  example, it may be set to 3600 to indicate that the service should run every hour
+  or 86400 to indicate that the service should run every day.
+
 `rlimit <resource> <cur> <max>`
 > This applies the given rlimit to the service. rlimits are inherited by child
   processes, so this effectively applies the given rlimit to the process tree
@@ -298,6 +305,12 @@
   seclabel or computed based on the service executable file security context.
   For native executables see libcutils android\_get\_control\_socket().
 
+`timeout_period <seconds>`
+> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
+  here, so oneshot services do not automatically restart, however all other services will.
+  This is particularly useful for creating a periodic service combined with the restart_period
+  option described above.
+
 `user <username>`
 > Change to 'username' before exec'ing this service.
   Currently defaults to root.  (??? probably should default to nobody)
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 79cfbcb..eb86eb0 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -68,6 +68,7 @@
     bool CreateLogicalPartitions();
     bool MountPartition(fstab_rec* fstab_rec);
     bool MountPartitions();
+    bool IsDmLinearEnabled();
     bool GetBackingDmLinearDevices();
 
     virtual ListenerAction UeventCallback(const Uevent& uevent);
@@ -82,6 +83,7 @@
     std::string lp_metadata_partition_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
+    std::string super_partition_name_;
     std::unique_ptr<DeviceHandler> device_handler_;
     UeventListener uevent_listener_;
 };
@@ -120,30 +122,18 @@
     return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
 }
 
-static bool IsRecoveryMode() {
+static bool ForceNormalBoot() {
     static bool force_normal_boot = []() {
         std::string cmdline;
         android::base::ReadFileToString("/proc/cmdline", &cmdline);
         return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
     }();
 
-    return !force_normal_boot && access("/system/bin/recovery", F_OK) == 0;
+    return force_normal_boot;
 }
 
-static inline bool IsDmLinearEnabled() {
-    static bool checked = false;
-    static bool enabled = false;
-    if (checked) {
-        return enabled;
-    }
-    import_kernel_cmdline(false,
-                          [](const std::string& key, const std::string& value, bool in_qemu) {
-                              if (key == "androidboot.logical_partitions" && value == "1") {
-                                  enabled = true;
-                              }
-                          });
-    checked = true;
-    return enabled;
+static bool IsRecoveryMode() {
+    return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
 }
 
 // Class Definitions
@@ -164,6 +154,8 @@
     device_handler_ = std::make_unique<DeviceHandler>(
             std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
             std::move(boot_devices), false);
+
+    super_partition_name_ = fs_mgr_get_super_partition_name();
 }
 
 std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -194,13 +186,20 @@
     return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
 }
 
+bool FirstStageMount::IsDmLinearEnabled() {
+    for (auto fstab_rec : mount_fstab_recs_) {
+        if (fs_mgr_is_logical(fstab_rec)) return true;
+    }
+    return false;
+}
+
 bool FirstStageMount::GetBackingDmLinearDevices() {
     // Add any additional devices required for dm-linear mappings.
     if (!IsDmLinearEnabled()) {
         return true;
     }
 
-    required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+    required_devices_partition_names_.emplace(super_partition_name_);
     return true;
 }
 
@@ -266,7 +265,7 @@
 
     if (lp_metadata_partition_.empty()) {
         LOG(ERROR) << "Could not locate logical partition tables in partition "
-                   << LP_METADATA_PARTITION_NAME;
+                   << super_partition_name_;
         return false;
     }
     return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
@@ -279,7 +278,7 @@
     auto iter = required_devices_partition_names_.find(name);
     if (iter != required_devices_partition_names_.end()) {
         LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
-        if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+        if (IsDmLinearEnabled() && name == super_partition_name_) {
             std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
             lp_metadata_partition_ = links[0];
         }
@@ -368,11 +367,15 @@
     // this case, we mount system first then pivot to it.  From that point on,
     // we are effectively identical to a system-as-root device.
     auto system_partition =
-            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(), [](const auto& rec) {
-                return rec->mount_point == "/system"s ||
-                       rec->mount_point == "/system_recovery_mount"s;
-            });
+            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(),
+                         [](const auto& rec) { return rec->mount_point == "/system"s; });
+
     if (system_partition != mount_fstab_recs_.end()) {
+        if (ForceNormalBoot()) {
+            free((*system_partition)->mount_point);
+            (*system_partition)->mount_point = strdup("/system_recovery_mount");
+        }
+
         if (!MountPartition(*system_partition)) {
             return false;
         }
@@ -388,7 +391,13 @@
         }
     }
 
-    fs_mgr_overlayfs_mount_all();
+    // heads up for instantiating required device(s) for overlayfs logic
+    const auto devices = fs_mgr_overlayfs_required_devices(device_tree_fstab_.get());
+    for (auto const& device : devices) {
+        InitMappedDevice(device);
+    }
+
+    fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
 
     return true;
 }
diff --git a/init/init.cpp b/init/init.cpp
index 3ab0a52..42ec88c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,6 +41,7 @@
 #include <cutils/android_reboot.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
+#include <selinux/android.h>
 
 #ifndef RECOVERY
 #include <binder/ProcessState.h>
@@ -185,23 +187,34 @@
     }
 }
 
-static std::optional<boot_clock::time_point> RestartProcesses() {
-    std::optional<boot_clock::time_point> next_process_restart_time;
+static std::optional<boot_clock::time_point> HandleProcessActions() {
+    std::optional<boot_clock::time_point> next_process_action_time;
     for (const auto& s : ServiceList::GetInstance()) {
+        if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
+            auto timeout_time = s->time_started() + *s->timeout_period();
+            if (boot_clock::now() > timeout_time) {
+                s->Timeout();
+            } else {
+                if (!next_process_action_time || timeout_time < *next_process_action_time) {
+                    next_process_action_time = timeout_time;
+                }
+            }
+        }
+
         if (!(s->flags() & SVC_RESTARTING)) continue;
 
-        auto restart_time = s->time_started() + 5s;
+        auto restart_time = s->time_started() + s->restart_period();
         if (boot_clock::now() > restart_time) {
             if (auto result = s->Start(); !result) {
                 LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
             }
         } else {
-            if (!next_process_restart_time || restart_time < *next_process_restart_time) {
-                next_process_restart_time = restart_time;
+            if (!next_process_action_time || restart_time < *next_process_action_time) {
+                next_process_action_time = restart_time;
             }
         }
     }
-    return next_process_restart_time;
+    return next_process_action_time;
 }
 
 static Result<Success> DoControlStart(Service* service) {
@@ -344,12 +357,12 @@
     if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
         return;
     }
-
-    std::string value = GetProperty("ro.boot.verifiedbootstate", "");
-
-    if (!value.empty()) {
-        property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
-    }
+    import_kernel_cmdline(
+            false, [](const std::string& key, const std::string& value, bool in_qemu) {
+                if (key == "androidboot.verifiedbootstate") {
+                    property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
+                }
+            });
 }
 
 static void export_kernel_boot_props() {
@@ -568,21 +581,41 @@
     RebootSystem(ANDROID_RB_RESTART2, "bootloader");
 }
 
-static void InitKernelLogging(char* argv[]) {
-    // Make stdin/stdout/stderr all point to /dev/null.
-    int fd = open("/sys/fs/selinux/null", O_RDWR);
-    if (fd == -1) {
-        int saved_errno = errno;
-        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
-        errno = saved_errno;
-        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
-    }
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    if (fd > 2) close(fd);
+static void GlobalSeccomp() {
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
+                                    bool in_qemu) {
+        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+            LOG(FATAL) << "Failed to globally enable seccomp!";
+        }
+    });
+}
 
-    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+static void SetupSelinux(char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+    });
+
+    // Set up SELinux, loading the SELinux policy.
+    SelinuxSetupKernelLogging();
+    SelinuxInitialize();
+
+    // We're in the kernel domain and want to transition to the init domain.  File systems that
+    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+    // but other file systems do.  In particular, this is needed for ramdisks such as the
+    // recovery image for A/B devices.
+    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+    }
+
+    setenv("SELINUX_INITIALIZED", "true", 1);
+
+    const char* path = "/system/bin/init";
+    const char* args[] = {path, nullptr};
+    execv(path, const_cast<char**>(args));
+
+    // execv() only returns if an error happened, in which case we
+    // panic and never return from this function.
+    PLOG(FATAL) << "execv(\"" << path << "\") failed";
 }
 
 int main(int argc, char** argv) {
@@ -600,9 +633,17 @@
         InstallRebootSignalHandlers();
     }
 
-    InitKernelLogging(argv);
+    if (getenv("SELINUX_INITIALIZED") == nullptr) {
+        SetupSelinux(argv);
+    }
+
+    // We need to set up stdin/stdout/stderr again now that we're running in init's context.
+    InitKernelLogging(argv, InitAborter);
     LOG(INFO) << "init second stage started!";
 
+    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+    GlobalSeccomp();
+
     // Set up a session keyring that all processes will have access to. It
     // will hold things like FBE encryption keys. No process should override
     // its session keyring.
@@ -631,6 +672,7 @@
     if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
     // Clean up our environment.
+    unsetenv("SELINUX_INITIALIZED");
     unsetenv("INIT_STARTED_AT");
     unsetenv("INIT_SELINUX_TOOK");
     unsetenv("INIT_AVB_VERSION");
@@ -723,12 +765,12 @@
         }
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
             if (!shutting_down) {
-                auto next_process_restart_time = RestartProcesses();
+                auto next_process_action_time = HandleProcessActions();
 
                 // If there's a process that needs restarting, wake up in time for that.
-                if (next_process_restart_time) {
+                if (next_process_action_time) {
                     epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
-                        *next_process_restart_time - boot_clock::now());
+                            *next_process_action_time - boot_clock::now());
                     if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                 }
             }
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 466cde3..d81ca5c 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <paths.h>
-#include <seccomp_policy.h>
 #include <stdlib.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
@@ -30,11 +29,9 @@
 #include <android-base/logging.h>
 #include <cutils/android_reboot.h>
 #include <private/android_filesystem_config.h>
-#include <selinux/android.h>
 
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
-#include "selinux.h"
 #include "util.h"
 
 using android::base::boot_clock;
@@ -42,15 +39,6 @@
 namespace android {
 namespace init {
 
-static void GlobalSeccomp() {
-    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
-                                    bool in_qemu) {
-        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
-            LOG(FATAL) << "Failed to globally enable seccomp!";
-        }
-    });
-}
-
 int main(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -96,6 +84,9 @@
     CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
     CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
 
+    // These below mounts are done in first stage init so that first stage mount can mount
+    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,
+    // should be done in rc files.
     // Mount staging areas for devices managed by vold
     // See storage config details at http://source.android.com/devices/storage/
     CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
@@ -111,9 +102,11 @@
 
     // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
     // talk to the outside world...
-    android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
-        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
-    });
+    // We need to set up stdin/stdout/stderr for child processes forked from first
+    // stage init as part of the mount process.  This closes /dev/console if the
+    // kernel had previously opened it.
+    auto reboot_bootloader = [](const char*) { RebootSystem(ANDROID_RB_RESTART2, "bootloader"); };
+    InitKernelLogging(argv, reboot_bootloader);
 
     if (!errors.empty()) {
         for (const auto& [error_string, error_errno] : errors) {
@@ -130,22 +123,6 @@
 
     SetInitAvbVersionInRecovery();
 
-    // Does this need to be done in first stage init or can it be done later?
-    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
-    GlobalSeccomp();
-
-    // Set up SELinux, loading the SELinux policy.
-    SelinuxSetupKernelLogging();
-    SelinuxInitialize();
-
-    // We're in the kernel domain and want to transition to the init domain when we exec second
-    // stage init.  File systems that store SELabels in their xattrs, such as ext4 do not need an
-    // explicit restorecon here, but other file systems do.  In particular, this is needed for
-    // ramdisks such as the recovery image for A/B devices.
-    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
-        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
-    }
-
     static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
     uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
     setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index c8c47a8..a3baeb1 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -47,9 +47,9 @@
   public:
     EventHandler();
     EventHandler(const EventHandler&) = delete;
-    EventHandler(EventHandler&&);
+    EventHandler(EventHandler&&) noexcept;
     EventHandler& operator=(const EventHandler&) = delete;
-    EventHandler& operator=(EventHandler&&);
+    EventHandler& operator=(EventHandler&&) noexcept;
     ~EventHandler() noexcept;
 
     bool init();
@@ -64,11 +64,11 @@
 
 EventHandler::EventHandler() : fd_(-1) {}
 
-EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+EventHandler::EventHandler(EventHandler&& rval) noexcept : fd_(rval.fd_) {
     rval.fd_ = -1;
 }
 
-EventHandler& EventHandler::operator=(EventHandler&& rval) {
+EventHandler& EventHandler::operator=(EventHandler&& rval) noexcept {
     fd_ = rval.fd_;
     rval.fd_ = -1;
     return *this;
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f88121..866f40e 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -162,7 +162,7 @@
  */
 static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
                                    std::vector<MountEntry>* emulatedPartitions, bool dump) {
-    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
     if (fp == nullptr) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
         return false;
@@ -464,6 +464,12 @@
         cmd = ANDROID_RB_RESTART2;
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
+            // adb reboot fastboot should boot into bootloader for devices not
+            // supporting logical partitions.
+            if (reboot_target == "fastboot" &&
+                !android::base::GetBoolProperty("ro.boot.logical_partitions", false)) {
+                reboot_target = "bootloader";
+            }
             // When rebooting to the bootloader notify the bootloader writing
             // also the BCB.
             if (reboot_target == "bootloader") {
diff --git a/init/service.cpp b/init/service.cpp
index d20e90a..a3e5953 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -627,6 +627,15 @@
     return Success();
 }
 
+Result<Success> Service::ParseRestartPeriod(const std::vector<std::string>& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 5)) {
+        return Error() << "restart_period value must be an integer >= 5";
+    }
+    restart_period_ = std::chrono::seconds(period);
+    return Success();
+}
+
 Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
     seclabel_ = args[1];
     return Success();
@@ -650,6 +659,15 @@
     return Error() << "Invalid shutdown option";
 }
 
+Result<Success> Service::ParseTimeoutPeriod(const std::vector<std::string>& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 1)) {
+        return Error() << "timeout_period value must be an integer >= 1";
+    }
+    timeout_period_ = std::chrono::seconds(period);
+    return Success();
+}
+
 template <typename T>
 Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
@@ -757,12 +775,16 @@
                         {1,     1,    &Service::ParseOomScoreAdjust}},
         {"override",    {0,     0,    &Service::ParseOverride}},
         {"priority",    {1,     1,    &Service::ParsePriority}},
+        {"restart_period",
+                        {1,     1,    &Service::ParseRestartPeriod}},
         {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
         {"shutdown",    {1,     1,    &Service::ParseShutdown}},
         {"sigstop",     {0,     0,    &Service::ParseSigstop}},
         {"socket",      {3,     6,    &Service::ParseSocket}},
+        {"timeout_period",
+                        {1,     1,    &Service::ParseTimeoutPeriod}},
         {"user",        {1,     1,    &Service::ParseUser}},
         {"writepid",    {1,     kMax, &Service::ParseWritepid}},
     };
@@ -1022,6 +1044,18 @@
     }
 }
 
+void Service::Timeout() {
+    // All process state flags will be taken care of in Reap(), we really just want to kill the
+    // process here when it times out.  Oneshot processes will transition to be disabled, and
+    // all other processes will transition to be restarting.
+    LOG(INFO) << "Service '" << name_ << "' expired its timeout of " << timeout_period_->count()
+              << " seconds and will now be killed";
+    if (pid_) {
+        KillProcessGroup(SIGKILL);
+        NotifyStateChange("stopping");
+    }
+}
+
 void Service::Restart() {
     if (flags_ & SVC_RUNNING) {
         /* Stop, wait, then start the service. */
diff --git a/init/service.h b/init/service.h
index ea79a07..e8d5ead 100644
--- a/init/service.h
+++ b/init/service.h
@@ -21,7 +21,9 @@
 #include <sys/resource.h>
 #include <sys/types.h>
 
+#include <chrono>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -81,6 +83,7 @@
     void Reset();
     void Stop();
     void Terminate();
+    void Timeout();
     void Restart();
     void Reap(const siginfo_t& siginfo);
     void DumpState() const;
@@ -117,6 +120,8 @@
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
     unsigned long start_order() const { return start_order_; }
     void set_sigstop(bool value) { sigstop_ = value; }
+    std::chrono::seconds restart_period() const { return restart_period_; }
+    std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
 
   private:
@@ -153,11 +158,13 @@
     Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
     Result<Success> ParseNamespace(const std::vector<std::string>& args);
     Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
+    Result<Success> ParseRestartPeriod(const std::vector<std::string>& args);
     Result<Success> ParseSeclabel(const std::vector<std::string>& args);
     Result<Success> ParseSetenv(const std::vector<std::string>& args);
     Result<Success> ParseShutdown(const std::vector<std::string>& args);
     Result<Success> ParseSigstop(const std::vector<std::string>& args);
     Result<Success> ParseSocket(const std::vector<std::string>& args);
+    Result<Success> ParseTimeoutPeriod(const std::vector<std::string>& args);
     Result<Success> ParseFile(const std::vector<std::string>& args);
     Result<Success> ParseUser(const std::vector<std::string>& args);
     Result<Success> ParseWritepid(const std::vector<std::string>& args);
@@ -220,6 +227,9 @@
 
     bool sigstop_ = false;
 
+    std::chrono::seconds restart_period_ = 5s;
+    std::optional<std::chrono::seconds> timeout_period_;
+
     std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index c2a21d4..092c51c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -62,7 +62,9 @@
 Result<std::string> ReadMessage(int socket) {
     char buffer[kBufferSize] = {};
     auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
-    if (result <= 0) {
+    if (result == 0) {
+        return Error();
+    } else if (result < 0) {
         return ErrnoError();
     }
     return std::string(buffer, result);
@@ -175,6 +177,12 @@
 
         auto init_message = ReadMessage(init_fd_);
         if (!init_message) {
+            if (init_message.error_errno() == 0) {
+                // If the init file descriptor was closed, let's exit quietly. If
+                // this was accidental, init will restart us. If init died, this
+                // avoids calling abort(3) unnecessarily.
+                return;
+            }
             LOG(FATAL) << "Could not read message from init: " << init_message.error();
         }
 
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
index bb143f1..7e05a0a 100644
--- a/init/tokenizer.cpp
+++ b/init/tokenizer.cpp
@@ -1,5 +1,7 @@
 #include "tokenizer.h"
 
+#include <android-base/macros.h>
+
 namespace android {
 namespace init {
 
@@ -106,6 +108,7 @@
                     continue;
                 }
                 x++;
+                FALLTHROUGH_INTENDED;
             case '\n':
                     /* \ <lf> -> line continuation */
                 state->line++;
diff --git a/init/util.cpp b/init/util.cpp
index 105ac87..3781141 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -436,5 +436,21 @@
     return true;
 }
 
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function) {
+    // Make stdin/stdout/stderr all point to /dev/null.
+    int fd = open("/dev/null", O_RDWR);
+    if (fd == -1) {
+        int saved_errno = errno;
+        android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+        errno = saved_errno;
+        PLOG(FATAL) << "Couldn't open /dev/null";
+    }
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    if (fd > 2) close(fd);
+    android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 07e4864..53f4547 100644
--- a/init/util.h
+++ b/init/util.h
@@ -64,6 +64,8 @@
 
 bool IsLegalPropertyName(const std::string& name);
 
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function);
+
 }  // namespace init
 }  // namespace android
 
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index c42ae49..7f9a18a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -27,15 +27,6 @@
             enabled: false,
         },
     },
-
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
 }
 
 libbacktrace_sources = [
@@ -108,7 +99,7 @@
     whole_static_libs: ["libdemangle"],
 }
 
-cc_library_shared {
+cc_test_library {
     name: "libbacktrace_test",
     defaults: ["libbacktrace_common"],
     host_supported: true,
@@ -121,6 +112,17 @@
     shared_libs: [
         "libunwindstack",
     ],
+    relative_install_path: "backtrace_test_libs",
+
+    target: {
+        linux_glibc: {
+            // This forces the creation of eh_frame with unwind information
+            // for host.
+            cflags: [
+                "-fcxx-exceptions"
+            ],
+        },
+    },
 }
 
 //-------------------------------------------------------------------------
@@ -128,12 +130,12 @@
 //-------------------------------------------------------------------------
 cc_test {
     name: "backtrace_test",
+    isolated: true,
     defaults: ["libbacktrace_common"],
     host_supported: true,
     srcs: [
         "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
-        "GetPss.cpp",
     ],
 
     cflags: [
@@ -143,7 +145,6 @@
     ],
 
     shared_libs: [
-        "libbacktrace_test",
         "libbacktrace",
         "libbase",
         "liblog",
@@ -152,17 +153,10 @@
 
     group_static_libs: true,
 
-    target: {
-        android: {
-            cflags: ["-DENABLE_PSS_TESTS"],
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        linux_glibc: {
-            static_libs: ["libutils"],
-        },
-    },
+    // So that the dlopen can find the libbacktrace_test.so.
+    ldflags: [
+        "-Wl,--rpath,${ORIGIN}/../backtrace_test_libs",
+    ],
 
     test_suites: ["device-tests"],
     data: [
diff --git a/libbacktrace/BacktraceTest.h b/libbacktrace/BacktraceTest.h
new file mode 100644
index 0000000..c38af04
--- /dev/null
+++ b/libbacktrace/BacktraceTest.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_TEST_H
+#define _LIBBACKTRACE_BACKTRACE_TEST_H
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+class BacktraceTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    dl_handle_ = dlopen("libbacktrace_test.so", RTLD_NOW | RTLD_LOCAL);
+
+    test_level_one_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_one"));
+
+    test_level_two_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_two"));
+
+    test_level_three_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_three"));
+
+    test_level_four_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_four"));
+
+    test_recursive_call_ = reinterpret_cast<int (*)(int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_recursive_call"));
+
+    test_get_context_and_wait_ = reinterpret_cast<void (*)(void*, volatile int*)>(
+        dlsym(dl_handle_, "test_get_context_and_wait"));
+
+    test_signal_action_ =
+        reinterpret_cast<void (*)(int, siginfo_t*, void*)>(dlsym(dl_handle_, "test_signal_action"));
+
+    test_signal_handler_ =
+        reinterpret_cast<void (*)(int)>(dlsym(dl_handle_, "test_signal_handler"));
+  }
+
+  void SetUp() override {
+    ASSERT_TRUE(dl_handle_ != nullptr);
+    ASSERT_TRUE(test_level_one_ != nullptr);
+    ASSERT_TRUE(test_level_two_ != nullptr);
+    ASSERT_TRUE(test_level_three_ != nullptr);
+    ASSERT_TRUE(test_level_four_ != nullptr);
+    ASSERT_TRUE(test_recursive_call_ != nullptr);
+    ASSERT_TRUE(test_get_context_and_wait_ != nullptr);
+    ASSERT_TRUE(test_signal_action_ != nullptr);
+    ASSERT_TRUE(test_signal_handler_ != nullptr);
+  }
+
+ public:
+  static void* dl_handle_;
+  static int (*test_level_one_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_two_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_three_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_four_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_recursive_call_)(int, void (*)(void*), void*);
+  static void (*test_get_context_and_wait_)(void*, volatile int*);
+  static void (*test_signal_action_)(int, siginfo_t*, void*);
+  static void (*test_signal_handler_)(int);
+};
+
+#endif  // _LIBBACKTRACE_BACKTRACE_TEST_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
deleted file mode 100644
index 6d750ea..0000000
--- a/libbacktrace/GetPss.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-// This is an extremely simplified version of libpagemap.
-
-#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
-
-#define PAGEMAP_PRESENT(x)     (_BITS(x, 63, 1))
-#define PAGEMAP_SWAPPED(x)     (_BITS(x, 62, 1))
-#define PAGEMAP_SHIFT(x)       (_BITS(x, 55, 6))
-#define PAGEMAP_PFN(x)         (_BITS(x, 0, 55))
-#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
-#define PAGEMAP_SWAP_TYPE(x)   (_BITS(x, 0,  5))
-
-static bool ReadData(int fd, off_t place, uint64_t *data) {
-  if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
-    return false;
-  }
-  if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
-    return false;
-  }
-  return true;
-}
-
-size_t GetPssBytes() {
-  FILE* maps = fopen("/proc/self/maps", "r");
-  if (maps == nullptr) {
-    return 0;
-  }
-
-  int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
-  if (pagecount_fd == -1) {
-    fclose(maps);
-    return 0;
-  }
-
-  int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
-  if (pagemap_fd == -1) {
-    fclose(maps);
-    close(pagecount_fd);
-    return 0;
-  }
-
-  char line[4096];
-  size_t total_pss = 0;
-  int pagesize = getpagesize();
-  while (fgets(line, sizeof(line), maps)) {
-    uintptr_t start, end;
-    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
-      total_pss = 0;
-      break;
-    }
-    for (off_t page = static_cast<off_t>(start/pagesize);
-         page < static_cast<off_t>(end/pagesize); page++) {
-      uint64_t data;
-      if (ReadData(pagemap_fd, page, &data)) {
-        if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
-          uint64_t count;
-          if (ReadData(pagecount_fd, static_cast<off_t>(PAGEMAP_PFN(data)), &count)) {
-            total_pss += (count >= 1) ? pagesize / count : 0;
-          }
-        }
-      }
-    }
-  }
-
-  fclose(maps);
-
-  close(pagecount_fd);
-  close(pagemap_fd);
-
-  return total_pss;
-}
diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h
deleted file mode 100644
index 787c33d..0000000
--- a/libbacktrace/GetPss.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBBACKTRACE_GET_PSS_H
-#define _LIBBACKTRACE_GET_PSS_H
-
-size_t GetPssBytes();
-
-#endif // _LIBBACKTRACE_GET_PSS_H
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 7d1027e..662fb99 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -37,15 +37,7 @@
 
 #include <gtest/gtest.h>
 
-extern "C" {
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, void (*)(void*), void*);
-int test_level_two(int, int, int, int, void (*)(void*), void*);
-int test_level_three(int, int, int, int, void (*)(void*), void*);
-int test_level_four(int, int, int, int, void (*)(void*), void*);
-int test_recursive_call(int, void (*)(void*), void*);
-void test_get_context_and_wait(void* context, volatile int* exit_flag);
-}
+#include "BacktraceTest.h"
 
 struct FunctionSymbol {
   std::string name;
@@ -56,12 +48,13 @@
 static std::vector<FunctionSymbol> GetFunctionSymbols() {
   std::vector<FunctionSymbol> symbols = {
       {"unknown_start", 0, 0},
-      {"test_level_one", reinterpret_cast<uint64_t>(&test_level_one), 0},
-      {"test_level_two", reinterpret_cast<uint64_t>(&test_level_two), 0},
-      {"test_level_three", reinterpret_cast<uint64_t>(&test_level_three), 0},
-      {"test_level_four", reinterpret_cast<uint64_t>(&test_level_four), 0},
-      {"test_recursive_call", reinterpret_cast<uint64_t>(&test_recursive_call), 0},
-      {"test_get_context_and_wait", reinterpret_cast<uint64_t>(&test_get_context_and_wait), 0},
+      {"test_level_one", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_one_), 0},
+      {"test_level_two", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_two_), 0},
+      {"test_level_three", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_three_), 0},
+      {"test_level_four", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_four_), 0},
+      {"test_recursive_call", reinterpret_cast<uint64_t>(&BacktraceTest::test_recursive_call_), 0},
+      {"test_get_context_and_wait",
+       reinterpret_cast<uint64_t>(&BacktraceTest::test_get_context_and_wait_), 0},
       {"unknown_end", static_cast<uint64_t>(-1), static_cast<uint64_t>(-1)},
   };
   std::sort(
@@ -100,7 +93,7 @@
 static void* OfflineThreadFunc(void* arg) {
   OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
   fn_arg->tid = android::base::GetThreadId();
-  test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
+  BacktraceTest::test_get_context_and_wait_(&fn_arg->ucontext, &fn_arg->exit_flag);
   return nullptr;
 }
 
@@ -109,7 +102,7 @@
 }
 
 // This test is disable because it is for generating test data.
-TEST(libbacktrace, DISABLED_generate_offline_testdata) {
+TEST_F(BacktraceTest, DISABLED_generate_offline_testdata) {
   // Create a thread to generate the needed stack and registers information.
   const size_t stack_size = 16 * 1024;
   void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
@@ -304,22 +297,22 @@
 }
 
 // For now, these tests can only run on the given architectures.
-TEST(libbacktrace, offline_eh_frame) {
+TEST_F(BacktraceTest, offline_eh_frame) {
   BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
   BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
 }
 
-TEST(libbacktrace, offline_debug_frame) {
+TEST_F(BacktraceTest, offline_debug_frame) {
   BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
   BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
 }
 
-TEST(libbacktrace, offline_gnu_debugdata) {
+TEST_F(BacktraceTest, offline_gnu_debugdata) {
   BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
   BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
 }
 
-TEST(libbacktrace, offline_arm_exidx) {
+TEST_F(BacktraceTest, offline_arm_exidx) {
   BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
 }
 
@@ -373,32 +366,32 @@
 
 // This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
 // overlap with each other, which appears in /system/lib/libart.so.
-TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
+TEST_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
   LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
 }
 
-TEST(libbacktrace, offline_debug_frame_with_load_bias) {
+TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
   LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
 }
 
-TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
+TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
   LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
 }
 
-TEST(libbacktrace, offline_cie_with_P_augmentation) {
+TEST_F(BacktraceTest, offline_cie_with_P_augmentation) {
   // Make sure we can unwind through functions with CIE entry containing P augmentation, which
   // makes unwinding library reading personality handler from memory. One example is
   // /system/lib64/libskia.so.
   LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
 }
 
-TEST(libbacktrace, offline_empty_eh_frame_hdr) {
+TEST_F(BacktraceTest, offline_empty_eh_frame_hdr) {
   // Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
   // /vendor/lib64/egl/eglSubDriverAndroid.so.
   LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
 }
 
-TEST(libbacktrace, offline_max_frames_limit) {
+TEST_F(BacktraceTest, offline_max_frames_limit) {
   // The length of callchain can reach 256 when recording an application.
   ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
 }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 06a32c7..f4191b9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <malloc.h>
 #include <pthread.h>
 #include <signal.h>
 #include <stdint.h>
@@ -55,6 +56,7 @@
 
 // For the THREAD_SIGNAL definition.
 #include "BacktraceCurrent.h"
+#include "BacktraceTest.h"
 #include "backtrace_testlib.h"
 
 // Number of microseconds per milliseconds.
@@ -95,6 +97,23 @@
 static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
                           map_create_func_t map_func = nullptr);
 
+void* BacktraceTest::dl_handle_;
+int (*BacktraceTest::test_level_one_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_two_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_three_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_four_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_recursive_call_)(int, void (*)(void*), void*);
+void (*BacktraceTest::test_get_context_and_wait_)(void*, volatile int*);
+void (*BacktraceTest::test_signal_action_)(int, siginfo_t*, void*);
+void (*BacktraceTest::test_signal_handler_)(int);
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+  static const char* initial_args[] = {"--slow_threshold_ms=8000", "--deadline_threshold_ms=15000"};
+  *args = initial_args;
+  *num_args = 2;
+  return true;
+}
+
 static uint64_t NanoTime() {
   struct timespec t = { 0, 0 };
   clock_gettime(CLOCK_MONOTONIC, &t);
@@ -250,7 +269,7 @@
   return false;
 }
 
-TEST(libbacktrace, local_no_unwind_frames) {
+TEST_F(BacktraceTest, local_no_unwind_frames) {
   // Verify that a local unwind does not include any frames within
   // libunwind or libbacktrace.
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -270,7 +289,7 @@
   }
 }
 
-TEST(libbacktrace, local_unwind_frames) {
+TEST_F(BacktraceTest, local_unwind_frames) {
   // Verify that a local unwind with the skip frames disabled does include
   // frames within the backtrace libraries.
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -302,8 +321,8 @@
                                                << DumpFrames(backtrace.get());
 }
 
-TEST(libbacktrace, local_trace) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_trace) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
 }
 
 static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
@@ -357,12 +376,12 @@
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
 }
 
-TEST(libbacktrace, local_trace_ignore_frames) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
+TEST_F(BacktraceTest, local_trace_ignore_frames) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
 }
 
-TEST(libbacktrace, local_max_trace) {
-  ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_max_trace) {
+  ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxBacktrace, nullptr), 0);
 }
 
 static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
@@ -402,10 +421,10 @@
   ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
 }
 
-TEST(libbacktrace, ptrace_trace) {
+TEST_F(BacktraceTest, ptrace_trace) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
@@ -416,10 +435,10 @@
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-TEST(libbacktrace, ptrace_max_trace) {
+TEST_F(BacktraceTest, ptrace_max_trace) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
+    ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
@@ -446,10 +465,10 @@
   VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
 }
 
-TEST(libbacktrace, ptrace_ignore_frames) {
+TEST_F(BacktraceTest, ptrace_ignore_frames) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
@@ -462,7 +481,7 @@
 
 // Create a process with multiple threads and dump all of the threads.
 static void* PtraceThreadLevelRun(void*) {
-  EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+  EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
   return nullptr;
 }
 
@@ -483,7 +502,7 @@
   }
 }
 
-TEST(libbacktrace, ptrace_threads) {
+TEST_F(BacktraceTest, ptrace_threads) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
@@ -494,7 +513,7 @@
       pthread_t thread;
       ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
     }
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
 
@@ -532,8 +551,8 @@
   VerifyLevelDump(backtrace.get());
 }
 
-TEST(libbacktrace, thread_current_level) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_level) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
 }
 
 static void VerifyMaxThread(void*) {
@@ -545,19 +564,19 @@
   VerifyMaxDump(backtrace.get());
 }
 
-TEST(libbacktrace, thread_current_max) {
-  ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_max) {
+  ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxThread, nullptr), 0);
 }
 
 static void* ThreadLevelRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
   thread->tid = android::base::GetThreadId();
-  EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+  EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, ThreadSetState, data), 0);
   return nullptr;
 }
 
-TEST(libbacktrace, thread_level_trace) {
+TEST_F(BacktraceTest, thread_level_trace) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -607,7 +626,7 @@
   EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
 }
 
-TEST(libbacktrace, thread_ignore_frames) {
+TEST_F(BacktraceTest, thread_ignore_frames) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -644,11 +663,12 @@
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
   thread->tid = android::base::GetThreadId();
-  EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+  EXPECT_NE(BacktraceTest::test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, ThreadSetState, data),
+            0);
   return nullptr;
 }
 
-TEST(libbacktrace, thread_max_trace) {
+TEST_F(BacktraceTest, thread_max_trace) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -742,17 +762,17 @@
   }
 }
 
-TEST(libbacktrace, thread_multiple_dump) {
+TEST_F(BacktraceTest, thread_multiple_dump) {
   MultipleThreadDumpTest(false);
 }
 
-TEST(libbacktrace, thread_multiple_dump_same_map) {
+TEST_F(BacktraceTest, thread_multiple_dump_same_map) {
   MultipleThreadDumpTest(true);
 }
 
 // This test is for UnwindMaps that should share the same map cursor when
 // multiple maps are created for the current process at the same time.
-TEST(libbacktrace, simultaneous_maps) {
+TEST_F(BacktraceTest, simultaneous_maps) {
   BacktraceMap* map1 = BacktraceMap::Create(getpid());
   BacktraceMap* map2 = BacktraceMap::Create(getpid());
   BacktraceMap* map3 = BacktraceMap::Create(getpid());
@@ -779,7 +799,7 @@
   delete map3;
 }
 
-TEST(libbacktrace, fillin_erases) {
+TEST_F(BacktraceTest, fillin_erases) {
   BacktraceMap* back_map = BacktraceMap::Create(getpid());
 
   backtrace_map_t map;
@@ -798,7 +818,7 @@
   ASSERT_EQ("", map.name);
 }
 
-TEST(libbacktrace, format_test) {
+TEST_F(BacktraceTest, format_test) {
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
 
@@ -969,7 +989,7 @@
   ASSERT_TRUE(test_it == test_maps.end());
 }
 
-TEST(libbacktrace, verify_map_remote) {
+TEST_F(BacktraceTest, verify_map_remote) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1069,7 +1089,7 @@
   delete[] expected;
 }
 
-TEST(libbacktrace, thread_read) {
+TEST_F(BacktraceTest, thread_read) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -1120,7 +1140,7 @@
   }
 }
 
-TEST(libbacktrace, process_read) {
+TEST_F(BacktraceTest, process_read) {
   g_ready = 0;
   pid_t pid;
   if ((pid = fork()) == 0) {
@@ -1187,29 +1207,23 @@
 }
 
 static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
-  std::string system_dir;
-
-#if defined(__BIONIC__)
-  system_dir = "/system/lib";
-#else
-  const char* host_out_env = getenv("ANDROID_HOST_OUT");
-  ASSERT_TRUE(host_out_env != nullptr);
-  system_dir = std::string(host_out_env) + "/lib";
-#endif
-
-#if defined(__LP64__)
-  system_dir += "64";
-#endif
+  std::string test_lib(testing::internal::GetArgvs()[0]);
+  auto const value = test_lib.find_last_of('/');
+  if (value == std::string::npos) {
+    test_lib = "../backtrace_test_libs/";
+  } else {
+    test_lib = test_lib.substr(0, value + 1) + "../backtrace_test_libs/";
+  }
+  test_lib += "libbacktrace_test.so";
 
   *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
-  std::string cp_cmd =
-      android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+  std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
 
   // Copy the shared so to a tempory directory.
   ASSERT_EQ(0, system(cp_cmd.c_str()));
 }
 
-TEST(libbacktrace, check_unreadable_elf_local) {
+TEST_F(BacktraceTest, check_unreadable_elf_local) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1251,7 +1265,7 @@
   VerifyFunctionsFound(found_functions);
 }
 
-TEST(libbacktrace, check_unreadable_elf_remote) {
+TEST_F(BacktraceTest, check_unreadable_elf_remote) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1390,7 +1404,7 @@
 
 typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
 
-TEST(libbacktrace, unwind_through_unreadable_elf_local) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_local) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1405,11 +1419,9 @@
 
   ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace, reinterpret_cast<void*>(test_func)),
             0);
-
-  ASSERT_TRUE(dlclose(lib_handle) == 0);
 }
 
-TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_remote) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1428,7 +1440,6 @@
     exit(0);
   }
   ASSERT_TRUE(pid > 0);
-  ASSERT_TRUE(dlclose(lib_handle) == 0);
 
   uint64_t start = NanoTime();
   bool done = false;
@@ -1465,7 +1476,7 @@
   ASSERT_TRUE(done) << "Test function never found in unwind.";
 }
 
-TEST(libbacktrace, unwind_thread_doesnt_exist) {
+TEST_F(BacktraceTest, unwind_thread_doesnt_exist) {
   std::unique_ptr<Backtrace> backtrace(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
   ASSERT_TRUE(backtrace.get() != nullptr);
@@ -1473,18 +1484,18 @@
   ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
 }
 
-TEST(libbacktrace, local_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, local_get_function_name_before_unwind) {
   std::unique_ptr<Backtrace> backtrace(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
 
   // Verify that trying to get a function name before doing an unwind works.
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
   uint64_t offset;
   ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
 }
 
-TEST(libbacktrace, remote_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, remote_get_function_name_before_unwind) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1492,7 +1503,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
 
   // Verify that trying to get a function name before doing an unwind works.
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
   uint64_t offset;
   ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
 
@@ -1579,7 +1590,7 @@
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
 
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(BacktraceTest::test_level_one_) + 1;
   // Now verify the device map flag actually causes the function name to be empty.
   backtrace->FillInMap(cur_func_offset, &map);
   ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
@@ -1628,7 +1639,7 @@
   ASSERT_EQ(0U, backtrace->NumFrames());
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_local) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_local) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1642,7 +1653,7 @@
   munmap(device_map, DEVICE_MAP_SIZE);
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_remote) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_remote) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1698,13 +1709,13 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     if (use_action) {
-      ScopedSignalHandler ssh(SIGUSR1, test_signal_action);
+      ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_action_);
 
-      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+      BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
     } else {
-      ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);
+      ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_handler_);
 
-      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+      BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
     }
   }
   ASSERT_NE(-1, pid);
@@ -1805,11 +1816,11 @@
   FinishRemoteProcess(pid);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_handler) {
   UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_action) {
   UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
 }
 
@@ -1822,49 +1833,41 @@
   ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
 }
 
-TEST(libbacktrace, unwind_frame_skip_numbering) {
+TEST_F(BacktraceTest, unwind_frame_skip_numbering) {
   TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
 }
 
-#if defined(ENABLE_PSS_TESTS)
-#include "GetPss.h"
-
 #define MAX_LEAK_BYTES (32*1024UL)
 
 static void CheckForLeak(pid_t pid, pid_t tid) {
   std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
 
-  // Do a few runs to get the PSS stable.
-  for (size_t i = 0; i < 100; i++) {
-    Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
-    ASSERT_TRUE(backtrace != nullptr);
-    ASSERT_TRUE(backtrace->Unwind(0));
-    VERIFY_NO_ERROR(backtrace->GetError().error_code);
-    delete backtrace;
-  }
-  size_t stable_pss = GetPssBytes();
-  ASSERT_TRUE(stable_pss != 0);
-
   // Loop enough that even a small leak should be detectable.
+  size_t first_allocated_bytes = 0;
+  size_t last_allocated_bytes = 0;
   for (size_t i = 0; i < 4096; i++) {
     Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
     VERIFY_NO_ERROR(backtrace->GetError().error_code);
     delete backtrace;
-  }
-  size_t new_pss = GetPssBytes();
-  ASSERT_TRUE(new_pss != 0);
-  if (new_pss > stable_pss) {
-    ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+
+    size_t allocated_bytes = mallinfo().uordblks;
+    if (first_allocated_bytes == 0) {
+      first_allocated_bytes = allocated_bytes;
+    } else if (last_allocated_bytes > first_allocated_bytes) {
+      // Check that the memory did not increase too much over the first loop.
+      ASSERT_LE(last_allocated_bytes - first_allocated_bytes, MAX_LEAK_BYTES);
+    }
+    last_allocated_bytes = allocated_bytes;
   }
 }
 
-TEST(libbacktrace, check_for_leak_local) {
+TEST_F(BacktraceTest, check_for_leak_local) {
   CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
 }
 
-TEST(libbacktrace, check_for_leak_local_thread) {
+TEST_F(BacktraceTest, check_for_leak_local_thread) {
   thread_t thread_data = { 0, 0, 0, nullptr };
   pthread_t thread;
   ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
@@ -1880,7 +1883,7 @@
   ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
 }
 
-TEST(libbacktrace, check_for_leak_remote) {
+TEST_F(BacktraceTest, check_for_leak_remote) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1888,4 +1891,3 @@
 
   FinishRemoteProcess(pid);
 }
-#endif
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index bcc0616..d27feb9 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
+cc_library {
     name: "libgrallocusage",
     vendor_available: true,
     cflags: [
diff --git a/libgrallocusage/OWNERS b/libgrallocusage/OWNERS
new file mode 100644
index 0000000..154dc6d
--- /dev/null
+++ b/libgrallocusage/OWNERS
@@ -0,0 +1,3 @@
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2e2bf87..574a386 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -72,7 +72,7 @@
   explicit MapString(const std::string& str)
       : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
   }
-  MapString(MapString&& rval)
+  MapString(MapString&& rval) noexcept
       : alloc(rval.alloc), str(rval.data(), rval.length()) {
     rval.alloc = NULL;
   }
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index e7b1728..53653de 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -56,15 +56,24 @@
 /*
  * Use __VA_ARGS__ if running a static analyzer,
  * to avoid warnings of unused variables in __VA_ARGS__.
- * __FAKE_USE_VA_ARGS is undefined at link time,
- * so don't link with __clang_analyzer__ defined.
+ * Use contexpr function in C++ mode, so these macros can be used
+ * in other constexpr functions without warning.
  */
 #ifdef __clang_analyzer__
-extern void __fake_use_va_args(int, ...);
-#define __FAKE_USE_VA_ARGS(...) __fake_use_va_args(0, ##__VA_ARGS__)
+#ifdef __cplusplus
+extern "C++" {
+template <typename... Ts>
+constexpr int __fake_use_va_args(Ts...) {
+  return 0;
+}
+}
+#else
+extern int __fake_use_va_args(int, ...);
+#endif /* __cplusplus */
+#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
 #else
 #define __FAKE_USE_VA_ARGS(...) ((void)(0))
-#endif
+#endif /* __clang_analyzer__ */
 
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index d1f20f4..383d0e7 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -131,7 +131,7 @@
 
 static bool isLogdwActive() {
   std::string logdwSignature =
-      popenToString("grep /dev/socket/logdw /proc/net/unix");
+      popenToString("grep -a /dev/socket/logdw /proc/net/unix");
   size_t beginning = logdwSignature.find(' ');
   if (beginning == std::string::npos) return true;
   beginning = logdwSignature.find(' ', beginning + 1);
@@ -145,7 +145,7 @@
   end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
   std::string allLogdwEndpoints = popenToString(
-      "grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
+      "grep -a ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
       " ' /proc/net/unix | " +
       "sed -n 's/.* \\([0-9][0-9]*\\)$/ -> socket:[\\1]/p'");
   if (allLogdwEndpoints.length() == 0) return true;
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 5c7ec13..92a8325 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -52,7 +52,7 @@
         allocation_bytes_(0),
         roots_(allocator),
         root_vals_(allocator),
-        segv_handler_(allocator),
+        segv_handler_(),
         walking_ptr_(0) {
     valid_allocations_range_.end = 0;
     valid_allocations_range_.begin = ~valid_allocations_range_.end;
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index 69f320c..074dc48 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -57,7 +57,7 @@
 }
 
 void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
-  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+  std::function<void(SCCInfo*)> walk([&](SCCInfo* scc) {
     if (scc->accumulator != dominator) {
       scc->accumulator = dominator;
       dominator->cuumulative_size += scc->size;
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index adabfd8..b9dead5 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -33,12 +33,12 @@
   }
   ~ScopedPipe() { Close(); }
 
-  ScopedPipe(ScopedPipe&& other) {
+  ScopedPipe(ScopedPipe&& other) noexcept {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
   }
 
-  ScopedPipe& operator=(ScopedPipe&& other) {
+  ScopedPipe& operator=(ScopedPipe&& other) noexcept {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
     return *this;
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index ff53fad..9e08a8e 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -32,15 +32,14 @@
  public:
   using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
 
-  explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
+  explicit ScopedSignalHandler() : signal_(-1) {}
   ~ScopedSignalHandler() { reset(); }
 
   template <class F>
   void install(int signal, F&& f) {
     if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
 
-    handler_ = SignalFn(std::allocator_arg, allocator_,
-                        [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
+    handler_ = SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
 
     struct sigaction act {};
     act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
@@ -68,7 +67,6 @@
  private:
   using SignalFn = std::function<void(int, siginfo_t*, void*)>;
   DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
-  Allocator<Fn> allocator_;
   int signal_;
   struct sigaction old_act_;
   // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 355679f..f3ab652 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -38,7 +38,7 @@
 
   Node(T* ptr, Allocator<Node> allocator)
       : references_in(allocator), references_out(allocator), ptr(ptr){};
-  Node(Node&& rhs) = default;
+  Node(Node&& rhs) noexcept = default;
   void Edge(Node<T>* ref) {
     references_out.emplace(ref);
     ref->references_in.emplace(this);
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index e6e17ce..1551b5b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -54,12 +54,12 @@
 // -----------------------------------------------------------------------------
 cc_test {
     name: "metricslogger_tests",
+    isolated: true,
     defaults: ["metricslogger_defaults"],
     shared_libs: [
         "libbase",
         "libmetricslogger_debug",
     ],
-    static_libs: ["libBionicGtestMain"],
     srcs: [
         "metrics_logger_test.cpp",
     ],
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 5d174ae..a9832db 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -24,6 +24,8 @@
 #endif
 void reset_log_context(android_log_context ctx);
 int write_to_logger(android_log_context context, log_id_t id);
+void note_log_drop();
+void stats_log_close();
 
 #ifdef __cplusplus
 }
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 3d746db..735088a 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -119,6 +119,18 @@
     return retValue;
 }
 
+void note_log_drop() {
+    statsdLoggerWrite.noteDrop();
+}
+
+void stats_log_close() {
+    statsd_writer_init_lock();
+    if (statsdLoggerWrite.close) {
+        (*statsdLoggerWrite.close)();
+    }
+    statsd_writer_init_unlock();
+}
+
 /* log_init_lock assumed */
 static int __write_to_statsd_initialize_locked() {
     if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 9953bba..afe401f 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -38,6 +38,7 @@
 #define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static atomic_int dropped = 0;
 
 void statsd_writer_init_lock() {
     /*
@@ -59,14 +60,16 @@
 static int statsdOpen();
 static void statsdClose();
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+static void statsdNoteDrop();
 
 struct android_log_transport_write statsdLoggerWrite = {
-    .name = "statsd",
-    .sock = -EBADF,
-    .available = statsdAvailable,
-    .open = statsdOpen,
-    .close = statsdClose,
-    .write = statsdWrite,
+        .name = "statsd",
+        .sock = -EBADF,
+        .available = statsdAvailable,
+        .open = statsdOpen,
+        .close = statsdClose,
+        .write = statsdWrite,
+        .noteDrop = statsdNoteDrop,
 };
 
 /* log_init_lock assumed */
@@ -131,6 +134,10 @@
     return 1;
 }
 
+static void statsdNoteDrop() {
+    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+}
+
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
     ssize_t ret;
     int sock;
@@ -138,7 +145,6 @@
     struct iovec newVec[nr + headerLength];
     android_log_header_t header;
     size_t i, payloadSize;
-    static atomic_int dropped;
 
     sock = atomic_load(&statsdLoggerWrite.sock);
     if (sock < 0) switch (sock) {
@@ -252,8 +258,6 @@
 
     if (ret > (ssize_t)sizeof(header)) {
         ret -= sizeof(header);
-    } else if (ret == -EAGAIN) {
-        atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
     }
 
     return ret;
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 82e14e0..7289441 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -38,6 +38,8 @@
     void (*close)();    /* free up resources */
     /* write log to transport, returns number of bytes propagated, or -errno */
     int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+    /* note one log drop */
+    void (*noteDrop)();
 };
 
 #endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index be2145d..970e05c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -178,6 +178,7 @@
         "tests/JitDebugTest.cpp",
         "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
+        "tests/MapInfoCreateMemoryTest.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
         "tests/MapsTest.cpp",
@@ -188,6 +189,7 @@
         "tests/MemoryOfflineBufferTest.cpp",
         "tests/MemoryOfflineTest.cpp",
         "tests/MemoryRangeTest.cpp",
+        "tests/MemoryRangesTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
         "tests/RegsInfoTest.cpp",
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 6061f61..57a780e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -461,6 +461,7 @@
       if (reg == eval_info->cie->return_address_register) {
         eval_info->return_address_undefined = true;
       }
+      break;
     default:
       break;
   }
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 39378a3..64005ae 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -102,7 +102,54 @@
   if (!(flags & PROT_READ)) {
     return nullptr;
   }
-  return new MemoryRange(process_memory, start, end - start, 0);
+
+  // Need to verify that this elf is valid. It's possible that
+  // only part of the elf file to be mapped into memory is in the executable
+  // map. In this case, there will be another read-only map that includes the
+  // first part of the elf file. This is done if the linker rosegment
+  // option is used.
+  std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
+  bool valid;
+  uint64_t max_size;
+  Elf::GetInfo(memory.get(), &valid, &max_size);
+  if (valid) {
+    // Valid elf, we are done.
+    return memory.release();
+  }
+
+  if (name.empty() || maps_ == nullptr) {
+    return nullptr;
+  }
+
+  // Find the read-only map that has the same name and has an offset closest
+  // to the current offset but less than the offset of the current map.
+  // For shared libraries, there should be a r-x map that has a non-zero
+  // offset and then a r-- map that has a zero offset.
+  // For shared libraries loaded from an apk, there should be a r-x map that
+  // has a non-zero offset and then a r-- map that has a non-zero offset less
+  // than the offset from the r-x map.
+  uint64_t closest_offset = 0;
+  MapInfo* ro_map_info = nullptr;
+  for (auto map_info : *maps_) {
+    if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
+        map_info->offset >= closest_offset) {
+      ro_map_info = map_info;
+      closest_offset = ro_map_info->offset;
+    }
+  }
+
+  if (ro_map_info != nullptr) {
+    // Make sure that relative pc values are corrected properly.
+    elf_offset = offset - closest_offset;
+
+    MemoryRanges* ranges = new MemoryRanges;
+    ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
+                                   ro_map_info->end - ro_map_info->start, 0));
+    ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+    return ranges;
+  }
+  return nullptr;
 }
 
 Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index e676a5a..8729871 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -66,13 +66,13 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
       });
 }
 
 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
                const std::string& name, uint64_t load_bias) {
-  MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+  MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
   map_info->load_bias = load_bias;
   maps_.push_back(map_info);
 }
@@ -97,7 +97,7 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
       });
 }
 
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index beb2aad..cfa8c6d 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -316,6 +316,18 @@
   return memory_->Read(read_addr, dst, read_length);
 }
 
+void MemoryRanges::Insert(MemoryRange* memory) {
+  maps_.emplace(memory->offset() + memory->length(), memory);
+}
+
+size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
+  auto entry = maps_.upper_bound(addr);
+  if (entry != maps_.end()) {
+    return entry->second->Read(addr, dst, size);
+  }
+  return 0;
+}
+
 bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
   auto memory_file = std::make_shared<MemoryFileAtOffset>();
   if (!memory_file->Init(file, offset)) {
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index ac0df41..9755c48 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -29,20 +29,25 @@
 namespace unwindstack {
 
 // Forward declarations.
+class Maps;
 class Memory;
 
 struct MapInfo {
-  MapInfo() = default;
-  MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
-  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
-      : start(start),
+  MapInfo(Maps* maps) : maps_(maps) {}
+  MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
+  MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+          const char* name)
+      : maps_(maps),
+        start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
         load_bias(static_cast<uint64_t>(-1)) {}
-  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
-      : start(start),
+  MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+          const std::string& name)
+      : maps_(maps),
+        start(start),
         end(end),
         offset(offset),
         flags(flags),
@@ -50,6 +55,8 @@
         load_bias(static_cast<uint64_t>(-1)) {}
   ~MapInfo() = default;
 
+  Maps* maps_ = nullptr;
+
   uint64_t start = 0;
   uint64_t end = 0;
   uint64_t offset = 0;
@@ -69,14 +76,14 @@
 
   uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
 
+  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
  private:
   MapInfo(const MapInfo&) = delete;
   void operator=(const MapInfo&) = delete;
 
   Memory* GetFileMemory();
 
-  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
-
   // Protect the creation of the elf object.
   std::mutex mutex_;
 };
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dee5e98..9c425cb 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -119,6 +120,9 @@
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
 
+  uint64_t offset() { return offset_; }
+  uint64_t length() { return length_; }
+
  private:
   std::shared_ptr<Memory> memory_;
   uint64_t begin_;
@@ -126,6 +130,19 @@
   uint64_t offset_;
 };
 
+class MemoryRanges : public Memory {
+ public:
+  MemoryRanges() = default;
+  virtual ~MemoryRanges() = default;
+
+  void Insert(MemoryRange* memory);
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
 class MemoryOffline : public Memory {
  public:
   MemoryOffline() = default;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 4dd8cb0..40f9f8e 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -120,7 +120,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(0, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -134,7 +134,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -148,7 +148,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -156,7 +156,7 @@
 TEST(DexFileTest, create_using_memory_empty_file) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -164,7 +164,7 @@
 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -178,7 +178,7 @@
 
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -200,7 +200,7 @@
 TEST(DexFileTest, get_method) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -227,7 +227,7 @@
 TEST(DexFileTest, get_method_empty) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 89331ea..1afd4ef 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -79,8 +79,8 @@
 
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
-  MapInfo info1(start, end, 0, 0x5, tf.path);
-  MapInfo info2(start, end, 0, 0x5, tf.path);
+  MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
 
   Elf* elf1 = info1.GetElf(memory_, true);
   ASSERT_TRUE(elf1->valid());
@@ -120,17 +120,17 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Will have an elf at offset 0 in file.
-  MapInfo info0_1(start, end, 0, 0x5, tf.path);
-  MapInfo info0_2(start, end, 0, 0x5, tf.path);
+  MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
   // Will have an elf at offset 0x100 in file.
-  MapInfo info100_1(start, end, 0x100, 0x5, tf.path);
-  MapInfo info100_2(start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
   // Will have an elf at offset 0x200 in file.
-  MapInfo info200_1(start, end, 0x200, 0x5, tf.path);
-  MapInfo info200_2(start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
   // Will have an elf at offset 0 in file.
-  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
 
   Elf* elf0_1 = info0_1.GetElf(memory_, true);
   ASSERT_TRUE(elf0_1->valid());
@@ -217,10 +217,10 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Multiple info sections at different offsets will have non-zero elf offsets.
-  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
-  MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
-  MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
 
   Elf* elf300_1 = info300_1.GetElf(memory_, true);
   ASSERT_TRUE(elf300_1->valid());
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 55fe16f..9a117b2 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -297,7 +297,7 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  MapInfo map_info(0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000);
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 866b5b4..2a73c7e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -32,8 +32,10 @@
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 
+#include "ElfTestUtils.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
@@ -94,7 +96,7 @@
 TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info(0x100, 0x100, 0, 0, elf_.path);
+  MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
@@ -112,7 +114,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -133,7 +135,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -156,7 +158,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -172,7 +174,7 @@
 }
 
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -192,27 +194,24 @@
   // Set up some memory so that a valid local memory object would
   // be returned if the file mapping fails, but the device check is incorrect.
   std::vector<uint8_t> buffer(1024);
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
+  uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
+  MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
 
-  info.flags = 0x8000;
-  info.name = "/dev/something";
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
 TEST_F(MapInfoCreateMemoryTest, process_memory) {
-  MapInfo info;
-  info.start = 0x2000;
-  info.end = 0x3000;
-  info.offset = 0;
+  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  std::vector<uint8_t> buffer(1024);
+  memcpy(buffer.data(), &ehdr, sizeof(ehdr));
 
   // Verify that the the process_memory object is used, so seed it
   // with memory.
-  std::vector<uint8_t> buffer(1024);
-  for (size_t i = 0; i < buffer.size(); i++) {
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     buffer[i] = i % 256;
   }
   memory_->SetMemory(info.start, buffer.data(), buffer.size());
@@ -222,7 +221,8 @@
 
   memset(buffer.data(), 0, buffer.size());
   ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
-  for (size_t i = 0; i < buffer.size(); i++) {
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
   }
 
@@ -230,4 +230,87 @@
   ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
 }
 
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2600, 0, PROT_READ, "/only/in/memory.so", 0);
+  maps.Add(0x3000, 0x5000, 0x4000, PROT_READ | PROT_EXEC, "/only/in/memory.so", 0);
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x1600 - sizeof(ehdr), 0xab);
+
+  // Set the memory in the r-x map.
+  memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
+
+  MapInfo* map_info = maps.Find(0x3000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(mem.get() != nullptr);
+  EXPECT_EQ(0x4000UL, map_info->elf_offset);
+  EXPECT_EQ(0x4000UL, map_info->offset);
+
+  // Verify that reading values from this memory works properly.
+  std::vector<uint8_t> buffer(0x4000);
+  size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1600UL, bytes);
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < bytes; i++) {
+    ASSERT_EQ(0xab, buffer[i]) << "Failed at byte " << i;
+  }
+
+  bytes = mem->Read(0x4000, buffer.data(), buffer.size());
+  ASSERT_EQ(0x2000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x5d, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2000, 0, PROT_READ, "/only/in/memory.apk", 0);
+  maps.Add(0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+  maps.Add(0x3000, 0x4000, 0xa000, PROT_READ, "/only/in/memory.apk", 0);
+  maps.Add(0x4000, 0x5000, 0xb000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+  // Setup an elf at offset 0x1000 in memory.
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x2000 - sizeof(ehdr), 0x12);
+  memory_->SetMemoryBlock(0x2000, 0x1000, 0x23);
+
+  // Setup an elf at offset 0x3000 in memory..
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
+  memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
+
+  MapInfo* map_info = maps.Find(0x4000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(mem.get() != nullptr);
+  EXPECT_EQ(0x1000UL, map_info->elf_offset);
+  EXPECT_EQ(0xb000UL, map_info->offset);
+
+  // Verify that reading values from this memory works properly.
+  std::vector<uint8_t> buffer(0x4000);
+  size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1000UL, bytes);
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < bytes; i++) {
+    ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+  }
+
+  bytes = mem->Read(0x1000, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x43, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 861b82f..918c028 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -69,7 +69,7 @@
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
-  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
   Elf* elf = info.GetElf(process_memory_, false);
@@ -78,7 +78,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
-  MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -92,7 +92,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
-  MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
 
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
@@ -106,7 +106,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
-  MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x4000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -122,7 +122,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
-  MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x6000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -138,7 +138,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -154,7 +154,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -170,7 +170,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, end_le_start) {
-  MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -197,7 +197,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x1000);
   memset(buffer.data(), 0, buffer.size());
@@ -226,7 +226,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -256,7 +256,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -284,7 +284,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -312,7 +312,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
-  MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
+  MapInfo info(nullptr, 0x9000, 0xa000, 0x1000, 0, "");
 
   // Create valid elf data in process memory only.
   Elf64_Ehdr ehdr;
@@ -333,7 +333,8 @@
 }
 
 TEST_F(MapInfoGetElfTest, check_device_maps) {
-  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+               "/dev/something");
 
   // Create valid elf data in process memory for this to verify that only
   // the name is causing invalid elf data.
@@ -378,7 +379,7 @@
   wait = true;
   // Create all of the threads and have them do the GetElf at the same time
   // to make it likely that a race will occur.
-  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 7e64a8a..f5ac6cb 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
     process_memory_.reset(memory_);
     elf_ = new ElfFake(new MemoryFake);
     elf_container_.reset(elf_);
-    map_info_.reset(new MapInfo(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
   }
 
   void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
 };
 
 TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
 }
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9622ba5..6bdd0b2 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -63,7 +63,7 @@
 }
 
 TEST(MapsTest, verify_parse_line) {
-  MapInfo info;
+  MapInfo info(nullptr);
 
   VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
   EXPECT_EQ(1U, info.start);
@@ -136,7 +136,7 @@
 }
 
 TEST(MapsTest, verify_large_values) {
-  MapInfo info;
+  MapInfo info(nullptr);
 #if defined(__LP64__)
   VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
   EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index 60936cd..5695dfc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -23,6 +23,17 @@
 
 namespace unwindstack {
 
+void MemoryFake::SetMemoryBlock(uint64_t addr, size_t length, uint8_t value) {
+  for (size_t i = 0; i < length; i++, addr++) {
+    auto entry = data_.find(addr);
+    if (entry != data_.end()) {
+      entry->second = value;
+    } else {
+      data_.insert({addr, value});
+    }
+  }
+}
+
 void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
   const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
   for (size_t i = 0; i < length; i++, addr++) {
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 764a6c3..20610a5 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -36,6 +36,8 @@
 
   void SetMemory(uint64_t addr, const void* memory, size_t length);
 
+  void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
+
   void SetData8(uint64_t addr, uint8_t value) {
     SetMemory(addr, &value, sizeof(value));
   }
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index cb1a0c9..2bac95b 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <stdint.h>
-#include <string.h>
 
 #include <memory>
 #include <vector>
@@ -28,30 +27,34 @@
 
 namespace unwindstack {
 
-TEST(MemoryRangeTest, read) {
-  std::vector<uint8_t> src(1024);
-  memset(src.data(), 0x4c, 1024);
-  MemoryFake* memory_fake = new MemoryFake;
-  std::shared_ptr<Memory> process_memory(memory_fake);
-  memory_fake->SetMemory(9001, src);
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    process_memory_.reset();
+    memory_fake_ = new MemoryFake;
+    process_memory_.reset(memory_fake_);
+  }
 
-  MemoryRange range(process_memory, 9001, src.size(), 0);
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_fake_ = nullptr;
+};
+
+TEST_F(MemoryRangeTest, read_fully) {
+  memory_fake_->SetMemoryBlock(9000, 2048, 0x4c);
+
+  MemoryRange range(process_memory_, 9001, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
-  for (size_t i = 0; i < 1024; i++) {
+  ASSERT_TRUE(range.ReadFully(0, dst.data(), dst.size()));
+  for (size_t i = 0; i < dst.size(); i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 }
 
-TEST(MemoryRangeTest, read_near_limit) {
-  std::vector<uint8_t> src(4096);
-  memset(src.data(), 0x4c, 4096);
-  MemoryFake* memory_fake = new MemoryFake;
-  std::shared_ptr<Memory> process_memory(memory_fake);
-  memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read_fully_near_limit) {
+  memory_fake_->SetMemoryBlock(0, 8192, 0x4c);
 
-  MemoryRange range(process_memory, 1000, 1024, 0);
+  MemoryRange range(process_memory_, 1000, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
   ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
@@ -68,7 +71,7 @@
   ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
 }
 
-TEST(MemoryRangeTest, read_overflow) {
+TEST_F(MemoryRangeTest, read_fully_overflow) {
   std::vector<uint8_t> buffer(100);
 
   std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
@@ -76,19 +79,28 @@
   ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
 }
 
-TEST(MemoryRangeTest, Read) {
-  std::vector<uint8_t> src(4096);
-  memset(src.data(), 0x4c, 4096);
-  MemoryFake* memory_fake = new MemoryFake;
-  std::shared_ptr<Memory> process_memory(memory_fake);
-  memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read) {
+  memory_fake_->SetMemoryBlock(0, 4096, 0x4c);
 
-  MemoryRange range(process_memory, 1000, 1024, 0);
+  MemoryRange range(process_memory_, 1000, 1024, 0);
+
   std::vector<uint8_t> dst(1024);
-  ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+  ASSERT_EQ(4U, range.Read(1020, dst.data(), dst.size()));
   for (size_t i = 0; i < 4; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 }
 
+TEST_F(MemoryRangeTest, read_non_zero_offset) {
+  memory_fake_->SetMemoryBlock(1000, 1024, 0x12);
+
+  MemoryRange range(process_memory_, 1000, 1024, 400);
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_EQ(1024U, range.Read(400, dst.data(), dst.size()));
+  for (size_t i = 0; i < dst.size(); i++) {
+    ASSERT_EQ(0x12U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
new file mode 100644
index 0000000..d24fcd2
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryRangesTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    MemoryFake* memory = new MemoryFake;
+    process_memory_.reset(memory);
+    memory->SetMemoryBlock(1000, 5000, 0x15);
+    memory->SetMemoryBlock(6000, 12000, 0x26);
+    memory->SetMemoryBlock(14000, 20000, 0x37);
+    memory->SetMemoryBlock(20000, 22000, 0x48);
+
+    ranges_.reset(new MemoryRanges);
+    ranges_->Insert(new MemoryRange(process_memory_, 15000, 100, 4000));
+    ranges_->Insert(new MemoryRange(process_memory_, 10000, 2000, 2000));
+    ranges_->Insert(new MemoryRange(process_memory_, 3000, 1000, 0));
+    ranges_->Insert(new MemoryRange(process_memory_, 19000, 1000, 6000));
+    ranges_->Insert(new MemoryRange(process_memory_, 20000, 1000, 7000));
+  }
+
+  std::shared_ptr<Memory> process_memory_;
+  std::unique_ptr<MemoryRanges> ranges_;
+};
+
+TEST_F(MemoryRangesTest, read) {
+  std::vector<uint8_t> dst(2000);
+  size_t bytes = ranges_->Read(0, dst.data(), dst.size());
+  ASSERT_EQ(1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x15U, dst[i]) << "Failed at byte " << i;
+  }
+
+  bytes = ranges_->Read(2000, dst.data(), dst.size());
+  ASSERT_EQ(2000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x26U, dst[i]) << "Failed at byte " << i;
+  }
+
+  bytes = ranges_->Read(4000, dst.data(), dst.size());
+  ASSERT_EQ(100UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryRangesTest, read_fail) {
+  std::vector<uint8_t> dst(4096);
+  ASSERT_EQ(0UL, ranges_->Read(1000, dst.data(), dst.size()));
+  ASSERT_EQ(0UL, ranges_->Read(5000, dst.data(), dst.size()));
+  ASSERT_EQ(0UL, ranges_->Read(8000, dst.data(), dst.size()));
+}
+
+TEST_F(MemoryRangesTest, read_across_ranges) {
+  // The MemoryRanges object does not support reading across a range,
+  // so this will only read in the first range.
+  std::vector<uint8_t> dst(4096);
+  size_t bytes = ranges_->Read(6000, dst.data(), dst.size());
+  ASSERT_EQ(1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 90c3fe6..00264c2 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
   RegsX86_64 regs_x86_64;
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
-  MapInfo map_info(0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000);
   Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2428f68..4369030 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -58,51 +58,54 @@
  protected:
   static void SetUpTestCase() {
     maps_.FakeClear();
-    MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+    MapInfo* info =
+        new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
     ElfFake* elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
+    info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+    info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
                        "/dev/fake_device");
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
+    info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
+                       "/system/fake/libunwind.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
+    info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
+    info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
+    info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
+    info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+    info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake.vdex");
     info->load_bias = 0;
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+    info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
                        "/fake/fake_load_bias.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
@@ -110,7 +113,7 @@
     elf->FakeSetLoadBias(0x5000);
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+    info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
                        "/fake/fake_offset.oat");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 583c6b9..5feb2aa 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -62,11 +62,17 @@
 }
 
 // Move Constructor.
-FileMap::FileMap(FileMap&& other)
-    : mFileName(other.mFileName), mBasePtr(other.mBasePtr), mBaseLength(other.mBaseLength),
-      mDataOffset(other.mDataOffset), mDataPtr(other.mDataPtr), mDataLength(other.mDataLength)
+FileMap::FileMap(FileMap&& other) noexcept
+    : mFileName(other.mFileName),
+      mBasePtr(other.mBasePtr),
+      mBaseLength(other.mBaseLength),
+      mDataOffset(other.mDataOffset),
+      mDataPtr(other.mDataPtr),
+      mDataLength(other.mDataLength)
 #if defined(__MINGW32__)
-      , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
+      ,
+      mFileHandle(other.mFileHandle),
+      mFileMapping(other.mFileMapping)
 #endif
 {
     other.mFileName = nullptr;
@@ -79,7 +85,7 @@
 }
 
 // Move assign operator.
-FileMap& FileMap::operator=(FileMap&& other) {
+FileMap& FileMap::operator=(FileMap&& other) noexcept {
     mFileName = other.mFileName;
     mBasePtr = other.mBasePtr;
     mBaseLength = other.mBaseLength;
diff --git a/libutils/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
index 8d402a3..f9f8f3c 100644
--- a/libutils/include/utils/FileMap.h
+++ b/libutils/include/utils/FileMap.h
@@ -52,8 +52,8 @@
 public:
     FileMap(void);
 
-    FileMap(FileMap&& f);
-    FileMap& operator=(FileMap&& f);
+    FileMap(FileMap&& f) noexcept;
+    FileMap& operator=(FileMap&& f) noexcept;
 
     /*
      * Create a new mapping on an open file.
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 3abce17..1571129 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -56,7 +56,7 @@
 
     sp(T* other);  // NOLINT(implicit)
     sp(const sp<T>& other);
-    sp(sp<T>&& other);
+    sp(sp<T>&& other) noexcept;
     template<typename U> sp(U* other);  // NOLINT(implicit)
     template<typename U> sp(const sp<U>& other);  // NOLINT(implicit)
     template<typename U> sp(sp<U>&& other);  // NOLINT(implicit)
@@ -67,7 +67,7 @@
 
     sp& operator = (T* other);
     sp& operator = (const sp<T>& other);
-    sp& operator = (sp<T>&& other);
+    sp& operator=(sp<T>&& other) noexcept;
 
     template<typename U> sp& operator = (const sp<U>& other);
     template<typename U> sp& operator = (sp<U>&& other);
@@ -125,9 +125,8 @@
         m_ptr->incStrong(this);
 }
 
-template<typename T>
-sp<T>::sp(sp<T>&& other)
-        : m_ptr(other.m_ptr) {
+template <typename T>
+sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
     other.m_ptr = nullptr;
 }
 
@@ -169,8 +168,8 @@
     return *this;
 }
 
-template<typename T>
-sp<T>& sp<T>::operator =(sp<T>&& other) {
+template <typename T>
+sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
     if (oldPtr) oldPtr->decStrong(this);
     if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index c350a27..0e0caf1 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -91,10 +91,10 @@
   explicit ZipWriter(FILE* f);
 
   // Move constructor.
-  ZipWriter(ZipWriter&& zipWriter);
+  ZipWriter(ZipWriter&& zipWriter) noexcept;
 
   // Move assignment.
-  ZipWriter& operator=(ZipWriter&& zipWriter);
+  ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
 
   /**
    * Starts a new zip entry with the given path and flags.
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index add6e14..6a3db6b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -122,21 +122,36 @@
 #endif
 }
 
+static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
+                             const ZipStringOffset& zip_string_offset) {
+  const ZipString from_offset = zip_string_offset.GetZipString(start);
+  return from_offset == zip_string;
+}
+
+/**
+ * Returns offset of ZipString#name from the start of the central directory in the memory map.
+ * For valid ZipStrings contained in the zip archive mmap, 0 < offset < 0xffffff.
+ */
+static inline uint32_t GetOffset(const uint8_t* name, const uint8_t* start) {
+  CHECK_GT(name, start);
+  CHECK_LT(name, start + 0xffffff);
+  return static_cast<uint32_t>(name - start);
+}
+
 /*
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
-                            const ZipString& name) {
+static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
+                            const ZipString& name, const uint8_t* start) {
   const uint32_t hash = ComputeHash(name);
 
   // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
   uint32_t ent = hash & (hash_table_size - 1);
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
+  while (hash_table[ent].name_offset != 0) {
+    if (isZipStringEqual(start, name, hash_table[ent])) {
       return ent;
     }
-
     ent = (ent + 1) & (hash_table_size - 1);
   }
 
@@ -147,8 +162,8 @@
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
-                         const ZipString& name) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size,
+                         const ZipString& name, const uint8_t* start) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -156,20 +171,26 @@
    * We over-allocated the table, so we're guaranteed to find an empty slot.
    * Further, we guarantee that the hashtable size is not 0.
    */
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
+  while (hash_table[ent].name_offset != 0) {
+    if (isZipStringEqual(start, name, hash_table[ent])) {
       // We've found a duplicate entry. We don't accept it
       ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
       return kDuplicateEntry;
     }
     ent = (ent + 1) & (hash_table_size - 1);
   }
-
-  hash_table[ent].name = name.name;
+  hash_table[ent].name_offset = GetOffset(name.name, start);
   hash_table[ent].name_length = name.name_length;
   return 0;
 }
 
+#if defined(__BIONIC__)
+uint64_t GetOwnerTag(const ZipArchive* archive) {
+  return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
+                                        reinterpret_cast<uint64_t>(archive));
+}
+#endif
+
 ZipArchive::ZipArchive(const int fd, bool assume_ownership)
     : mapped_zip(fd),
       close_file(assume_ownership),
@@ -181,7 +202,7 @@
       hash_table(nullptr) {
 #if defined(__BIONIC__)
   if (assume_ownership) {
-    android_fdsan_exchange_owner_tag(fd, 0, reinterpret_cast<uint64_t>(this));
+    android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
   }
 #endif
 }
@@ -199,7 +220,7 @@
 ZipArchive::~ZipArchive() {
   if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
 #if defined(__BIONIC__)
-    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), reinterpret_cast<uint64_t>(this));
+    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
 #else
     close(mapped_zip.GetFileDescriptor());
 #endif
@@ -209,7 +230,8 @@
 }
 
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
-                                    off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
+                                    off64_t file_length, off64_t read_amount,
+                                    uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
 
   if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
@@ -362,7 +384,7 @@
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
   archive->hash_table =
-      reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
+      reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
   if (archive->hash_table == nullptr) {
     ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
           archive->hash_table_size, sizeof(ZipString));
@@ -418,7 +440,8 @@
     ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
-    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
+    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
+                                     archive->central_directory.GetBasePtr());
     if (add_result != 0) {
       ALOGW("Zip: Error adding entry to hash table %d", add_result);
       return add_result;
@@ -538,7 +561,9 @@
   // Recover the start of the central directory entry from the filename
   // pointer.  The filename is the first entry past the fixed-size data,
   // so we can just subtract back from that.
-  const uint8_t* ptr = archive->hash_table[ent].name;
+  const ZipString from_offset =
+      archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+  const uint8_t* ptr = from_offset.name;
   ptr -= sizeof(CentralDirectoryRecord);
 
   // This is the base of our mmapped region, we have to sanity check that
@@ -648,8 +673,9 @@
       ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
       return kIoError;
     }
-
-    if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
+    const ZipString from_offset =
+        archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+    if (memcmp(from_offset.name, name_buf.data(), nameLen)) {
       return kInconsistentInformation;
     }
 
@@ -747,19 +773,19 @@
     return kInvalidEntryName;
   }
 
-  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
-
+  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
+                                   archive->central_directory.GetBasePtr());
   if (ent < 0) {
     ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
     return ent;
   }
-
   return FindEntry(archive, ent, data);
 }
 
 int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
+    ALOGW("Zip: Null ZipArchiveHandle");
     return kInvalidHandle;
   }
 
@@ -771,19 +797,19 @@
 
   const uint32_t currentOffset = handle->position;
   const uint32_t hash_table_length = archive->hash_table_size;
-  const ZipString* hash_table = archive->hash_table;
-
+  const ZipStringOffset* hash_table = archive->hash_table;
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
-    if (hash_table[i].name != NULL &&
-        (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
-        (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
+    const ZipString from_offset =
+        hash_table[i].GetZipString(archive->central_directory.GetBasePtr());
+    if (hash_table[i].name_offset != 0 &&
+        (handle->prefix.name_length == 0 || from_offset.StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 || from_offset.EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
-        name->name = hash_table[i].name;
+        name->name = from_offset.name;
         name->name_length = hash_table[i].name_length;
       }
-
       return error;
     }
   }
@@ -877,7 +903,7 @@
     return FileWriter(fd, declared_length);
   }
 
-  FileWriter(FileWriter&& other)
+  FileWriter(FileWriter&& other) noexcept
       : fd_(other.fd_),
         declared_length_(other.declared_length_),
         total_bytes_written_(other.total_bytes_written_) {
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 0a73300..83cb11f 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -136,6 +136,26 @@
   size_t length_;
 };
 
+/**
+ * More space efficient string representation of strings in an mmaped zipped file than
+ * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
+ * space. ZipString stores a pointer to a string (on 64 bit, 8 bytes) and the length to read from
+ * that pointer, 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting 6 bytes.
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory mapped file instead
+ * of the entire address, consuming 8 bytes with alignment.
+ */
+struct ZipStringOffset {
+  uint32_t name_offset;
+  uint16_t name_length;
+
+  const ZipString GetZipString(const uint8_t* start) const {
+    ZipString zip_string;
+    zip_string.name = start + name_offset;
+    zip_string.name_length = name_length;
+    return zip_string;
+  }
+};
+
 struct ZipArchive {
   // open Zip archive
   mutable MappedZipFile mapped_zip;
@@ -154,7 +174,7 @@
   // allocate so the maximum number entries can never be higher than
   // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
   uint32_t hash_table_size;
-  ZipString* hash_table;
+  ZipStringOffset* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership);
   ZipArchive(void* address, size_t length);
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6ad3366..ed1d135 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -97,7 +97,7 @@
   }
 }
 
-ZipWriter::ZipWriter(ZipWriter&& writer)
+ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
     : file_(writer.file_),
       seekable_(writer.seekable_),
       current_offset_(writer.current_offset_),
@@ -109,7 +109,7 @@
   writer.state_ = State::kError;
 }
 
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
   file_ = writer.file_;
   seekable_ = writer.seekable_;
   current_offset_ = writer.current_offset_;
diff --git a/llkd/README.md b/llkd/README.md
index 1f69718..e5be850 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -127,7 +127,7 @@
 default 2 minutes samples of threads for D or Z.
 
 #### ro.llk.stack
-default *empty* or false, comma separated list of kernel symbols.
+default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
 The string "*false*" is the equivalent to an *empty* list.
 Look for kernel stack symbols that if ever persistently present can
 indicate a subsystem is locked up.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index d0188ec..1e2df2f 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -48,7 +48,7 @@
 /* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
 #define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
 #define LLK_CHECK_STACK_PROPERTY       "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT        ""
+#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages"
 #define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
     "0,1,2,init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
@@ -57,7 +57,7 @@
 #define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
 #define LLK_BLACKLIST_UID_DEFAULT      ""
 #define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
-#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,/system/bin/keystore"
+#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,/system/bin/keystore,ueventd"
 /* clang-format on */
 
 __END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 58c2ba8..6840ed0 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -265,7 +265,7 @@
     }
     content.erase(pos);
     uid_t ret;
-    if (!android::base::ParseInt(content, &ret, uid_t(0))) {
+    if (!android::base::ParseUint(content, &ret, uid_t(0))) {
         return -1;
     }
     return ret;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index d6b8ab3..469f6dc 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -531,7 +531,7 @@
         name = std::string_view(alloc->c_str(), alloc->size());
     }
 
-    explicit TagNameKey(TagNameKey&& rval)
+    explicit TagNameKey(TagNameKey&& rval) noexcept
         : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
         rval.alloc = nullptr;
     }
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
index 576a677..c3cf746 100644
--- a/mkbootimg/Android.bp
+++ b/mkbootimg/Android.bp
@@ -31,3 +31,34 @@
     header_libs: ["libmkbootimg_abi_headers"],
     export_header_lib_headers: ["libmkbootimg_abi_headers"],
 }
+
+python_defaults {
+    name: "mkbootimg_defaults",
+
+    version: {
+        py2: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+        py3: {
+            enabled: false,
+            embedded_launcher: false,
+        },
+    },
+}
+
+python_binary_host {
+    name: "mkbootimg",
+    defaults: ["mkbootimg_defaults"],
+    srcs: [
+        "mkbootimg.py",
+    ],
+}
+
+python_binary_host {
+    name: "unpack_bootimg",
+    defaults: ["mkbootimg_defaults"],
+    srcs: [
+        "unpack_bootimg.py",
+    ],
+}
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
deleted file mode 100644
index 92e1e27..0000000
--- a/mkbootimg/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := mkbootimg
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-
-LOCAL_MODULE := mkbootimg
-
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := unpack_bootimg
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-
-LOCAL_MODULE := unpack_bootimg
-
-include $(BUILD_PREBUILT)
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index bce308b..4432f9e 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -110,25 +110,25 @@
  */
 
 struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
-    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
-    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
+    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO/ACPIO image */
+    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
     uint32_t header_size;
 } __attribute__((packed));
 
 /* When the boot image header has a version of 1, the structure of the boot
  * image is as follows:
  *
- * +-----------------+
- * | boot header     | 1 page
- * +-----------------+
- * | kernel          | n pages
- * +-----------------+
- * | ramdisk         | m pages
- * +-----------------+
- * | second stage    | o pages
- * +-----------------+
- * | recovery dtbo   | p pages
- * +-----------------+
+ * +---------------------+
+ * | boot header         | 1 page
+ * +---------------------+
+ * | kernel              | n pages
+ * +---------------------+
+ * | ramdisk             | m pages
+ * +---------------------+
+ * | second stage        | o pages
+ * +---------------------+
+ * | recovery dtbo/acpio | p pages
+ * +---------------------+
  * n = (kernel_size + page_size - 1) / page_size
  * m = (ramdisk_size + page_size - 1) / page_size
  * o = (second_size + page_size - 1) / page_size
@@ -136,13 +136,14 @@
  *
  * 0. all entities are page_size aligned in flash
  * 1. kernel and ramdisk are required (size != 0)
- * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
+ * 2. recovery_dtbo/recovery_acpio is required for recovery.img in non-A/B
+ *    devices(recovery_dtbo_size != 0)
  * 3. second is optional (second_size == 0 -> no second)
  * 4. load each element (kernel, ramdisk, second) at
  *    the specified physical address (kernel_addr, etc)
- * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
- *    apply the correct set of overlays on the base device tree depending on the
- *    hardware/product revision.
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery
+ *    dtbo/acpio and apply the correct set of overlays on the base device tree
+ *    depending on the hardware/product revision.
  * 6. prepare tags at tag_addr.  kernel_args[] is
  *    appended to the kernel commandline in the tags.
  * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg.py
old mode 100755
new mode 100644
similarity index 95%
rename from mkbootimg/mkbootimg
rename to mkbootimg/mkbootimg.py
index fda9af0..2eb2bab
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg.py
@@ -161,7 +161,10 @@
                         required=True)
     parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
     parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
-    parser.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+    recovery_dtbo_group = parser.add_mutually_exclusive_group()
+    recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+    recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
+                                     type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
     parser.add_argument('--cmdline', help='extra arguments to be passed on the '
                         'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
     parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg.py
old mode 100755
new mode 100644
similarity index 100%
rename from mkbootimg/unpack_bootimg
rename to mkbootimg/unpack_bootimg.py
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b68dc34..a4c3955 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -77,7 +77,7 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data odm oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    sbin dev proc sys system data odm oem acct config storage mnt apex $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index f2892f8..d3e80c9 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -8,7 +8,6 @@
 dir.system = /system/bin/
 dir.system = /system/xbin/
 dir.system = /%PRODUCT%/bin/
-dir.system = /%PRODUCT_SERVICES%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -78,9 +77,9 @@
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
+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 +=           /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.permitted.paths  = /data
 namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -345,7 +344,9 @@
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
 namespace.system.asan.search.paths += /data/asan/product/${LIB}
-namespace.system.asan.search.paths +=           /product/${LIB}
+namespace.system.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 ###############################################################################
 # 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 db65c14..7e354ac 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,7 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -41,7 +41,8 @@
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
@@ -50,7 +51,9 @@
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
+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}
 
 ###############################################################################
 # "sphal" namespace
@@ -209,7 +212,8 @@
 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%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
@@ -230,7 +234,9 @@
 namespace.default.asan.search.paths += /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
+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 config for binaries under /postinstall.
@@ -243,4 +249,5 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 915540e..6a6a8f9 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -269,6 +269,12 @@
     # that they can be chown'd to system:system later on boot
     write /sys/class/leds/vibrator/trigger "transient"
 
+    # Setup APEX mount point and its security context
+    mount tmpfs tmpfs /apex nodev noexec nosuid
+    chmod 0755 /apex
+    chown root root /apex
+    restorecon /apex
+
 # Healthd can trigger a full boot from charger mode by signaling this
 # property when the power button is held.
 on property:sys.boot_from_charger_mode=1
@@ -400,6 +406,7 @@
 
     # Make sure we have the device encryption key.
     start vold
+    exec - system system -- /system/bin/vdc checkpoint prepareDriveForCheckpoint /data
     installkey /data
 
     # Start bootcharting as soon as possible after the data partition is
@@ -407,6 +414,9 @@
     mkdir /data/bootchart 0755 shell shell
     bootchart start
 
+    # Start apexd as soon as we can
+    start apexd
+
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
 
@@ -520,6 +530,8 @@
 
     mkdir /data/anr 0775 system system
 
+    mkdir /data/apex 0770 root root
+
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc
     mkdir /data/nfc/param 0770 nfc nfc
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 7466728..733b60f 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -62,7 +62,7 @@
         "uid_info.cpp",
         "storaged.proto",
         ":storaged_aidl",
-        "binder/android/os/storaged/IStoragedPrivate.aidl",
+        ":storaged_aidl_private",
     ],
 
     static_libs: ["libhealthhalutils"],
@@ -116,4 +116,13 @@
     srcs: [
         "binder/android/os/IStoraged.aidl",
     ],
+    path: "binder",
+}
+
+filegroup {
+    name: "storaged_aidl_private",
+    srcs: [
+        "binder/android/os/storaged/IStoragedPrivate.aidl",
+    ],
+    path: "binder",
 }
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 8e3b3b1..98cbcc3 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -21,6 +21,7 @@
 #include <cutils/log.h>
 #include <keymaster/android_keymaster_messages.h>
 #include <trusty_keymaster/TrustyKeymaster3Device.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
 
 using ::keymaster::AbortOperationRequest;
 using ::keymaster::AbortOperationResponse;
@@ -111,7 +112,8 @@
             }
         }
     }
-    KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} {
+    KmParamSet(KmParamSet&& other) noexcept
+        : keymaster_key_param_set_t{other.params, other.length} {
         other.length = 0;
         other.params = nullptr;
     }
@@ -393,20 +395,32 @@
                                             const hidl_vec<KeyParameter>& inParams,
                                             const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
     UpdateOperationRequest request;
-    request.op_handle = operationHandle;
-    request.input.Reinitialize(input.data(), input.size());
-    request.additional_params.Reinitialize(KmParamSet(inParams));
-
     UpdateOperationResponse response;
-    impl_->UpdateOperation(request, &response);
-
-    uint32_t resultConsumed = 0;
     hidl_vec<KeyParameter> resultParams;
     hidl_vec<uint8_t> resultBlob;
-    if (response.error == KM_ERROR_OK) {
-        resultConsumed = response.input_consumed;
-        resultParams = kmParamSet2Hidl(response.output_params);
-        resultBlob = kmBuffer2hidlVec(response.output);
+    uint32_t resultConsumed = 0;
+
+    request.op_handle = operationHandle;
+    request.additional_params.Reinitialize(KmParamSet(inParams));
+
+    size_t inp_size = input.size();
+    size_t ser_size = request.SerializedSize();
+
+    if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        response.error = KM_ERROR_INVALID_INPUT_LENGTH;
+    } else {
+        if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+            inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;
+        }
+        request.input.Reinitialize(input.data(), inp_size);
+
+        impl_->UpdateOperation(request, &response);
+
+        if (response.error == KM_ERROR_OK) {
+            resultConsumed = response.input_consumed;
+            resultParams = kmParamSet2Hidl(response.output_params);
+            resultBlob = kmBuffer2hidlVec(response.output);
+        }
     }
     _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
     return Void();