Merge "Create /metadata/password_slots during boot."
diff --git a/adb/Android.bp b/adb/Android.bp
index 3813578..1e085a7 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -402,6 +402,14 @@
         "liblog",
     ],
 
+    product_variables: {
+        debuggable: {
+            required: [
+                "remount",
+            ],
+        },
+    },
+
     target: {
         android: {
             srcs: [
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 5e6d416..ce494ee 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -14,339 +14,75 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-
 #include <errno.h>
 #include <fcntl.h>
-#include <mntent.h>
-#include <spawn.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <sys/mount.h>
-#include <sys/statvfs.h>
-#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
-#include <memory>
-#include <set>
 #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>
-#include <fs_mgr.h>
-#include <fs_mgr_overlayfs.h>
 
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "set_verity_enable_state_service.h"
 
-using android::base::Realpath;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::ReadDefaultFstab;
+static constexpr char kRemountCmd[] = "/system/bin/remount";
 
-// 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.
-static std::string find_proc_mount(const char* dir) {
-    std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
-    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 (canonical_path == e->mnt_dir) {
-            mnt_fsname = e->mnt_fsname;
-        }
-    }
-    return mnt_fsname;
-}
-
-// Returns the device used to mount a directory in the fstab.
-static std::string find_fstab_mount(const char* dir) {
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
-        return "";
-    }
-
-    auto entry = std::find_if(fstab.begin(), fstab.end(),
-                              [&dir](const auto& entry) { return entry.mount_point == dir; });
-    if (entry == fstab.end()) {
-        return "";
-    }
-    if (entry->fs_mgr_flags.logical) {
-        fs_mgr_update_logical_partition(&(*entry));
-    }
-    return entry->blk_device;
-}
-
-// The proc entry for / is full of lies, so check fstab instead.
-// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
-static std::string find_mount(const char* dir, bool is_root) {
-    if (is_root) {
-        return find_fstab_mount(dir);
-    } else {
-        return find_proc_mount(dir);
-    }
-}
-
-bool dev_is_overlayfs(const std::string& dev) {
-    return (dev == "overlay") || (dev == "overlayfs");
-}
-
-bool make_block_device_writable(const std::string& dev) {
-    if (dev_is_overlayfs(dev)) return true;
-    int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
+static bool do_remount(int fd, const std::string& cmd) {
+    if (getuid() != 0) {
+        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
         return false;
     }
 
-    int OFF = 0;
-    bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
-    unix_close(fd);
-    return result;
-}
-
-static bool can_unshare_blocks(int fd, const char* dev) {
-    const char* E2FSCK_BIN = "/system/bin/e2fsck";
-    if (access(E2FSCK_BIN, X_OK)) {
-        WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev);
+    auto pid = fork();
+    if (pid < 0) {
+        WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
         return false;
     }
 
-    pid_t child;
-    char* env[] = {nullptr};
-    const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr};
-    if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast<char**>(argv), env)) {
-        WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno));
+    if (pid == 0) {
+        // child side of the fork
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        dup2(fd, STDERR_FILENO);
+
+        execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
+        _exit(errno);
+    }
+
+    int wstatus = 0;
+    auto ret = waitpid(pid, &wstatus, 0);
+
+    if (ret == -1) {
+        WriteFdFmt(fd, "Failed to wait for %s: %s\n", kRemountCmd, strerror(errno));
+        return false;
+    } else if (ret != pid) {
+        WriteFdFmt(fd, "pid %d and waitpid return %d do not match for %s\n",
+                   static_cast<int>(pid), static_cast<int>(ret), kRemountCmd);
         return false;
     }
-    int status = 0;
-    int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
-    if (ret < 0) {
-        WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno));
+
+    if (WIFSIGNALED(wstatus)) {
+        WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
+                   strsignal(WTERMSIG(wstatus)));
         return false;
     }
-    if (!WIFEXITED(status)) {
-        WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+
+    if (!WIFEXITED(wstatus)) {
+        WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
         return false;
     }
-    int rc = WEXITSTATUS(status);
-    if (rc != 0) {
-        WriteFdFmt(fd,
-                   "%s is deduplicated, and an e2fsck check failed. It might not "
-                   "have enough free-space to be remounted as writable.\n",
-                   dev);
+
+    if (WEXITSTATUS(wstatus)) {
+        WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
         return false;
     }
+
     return true;
 }
 
-static unsigned long get_mount_flags(int fd, const char* dir) {
-    struct statvfs st_vfs;
-    if (statvfs(dir, &st_vfs) == -1) {
-        // Even though we could not get the original mount flags, assume that
-        // the mount was originally read-only.
-        WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
-        return MS_RDONLY;
-    }
-    return st_vfs.f_flag;
-}
-
-static bool remount_partition(int fd, const char* dir) {
-    if (!directory_exists(dir)) {
-        return true;
-    }
-    bool is_root = strcmp(dir, "/") == 0;
-    if (is_root && dev_is_overlayfs(find_mount("/system", false))) {
-        dir = "/system";
-        is_root = false;
-    }
-    std::string dev = find_mount(dir, is_root);
-    if (is_root && dev.empty()) {
-        // The fstab entry will be /system if the device switched roots during
-        // first-stage init.
-        dev = find_mount("/system", true);
-    }
-    // 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:
-    // the root will almost always be in a loop device, which is dynamic, so
-    // it's not convenient to put in the fstab.
-    if (dev.empty() && !is_root) {
-        return true;
-    }
-    if (!dev.empty() && !make_block_device_writable(dev)) {
-        WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
-                   dir, dev.c_str(), strerror(errno));
-        return false;
-    }
-
-    unsigned long remount_flags = get_mount_flags(fd, dir);
-    remount_flags &= ~MS_RDONLY;
-    remount_flags |= MS_REMOUNT;
-
-    if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) {
-        // This is useful for cases where the superblock is already marked as
-        // read-write, but the mount itself is read-only, such as containers
-        // where the remount with just MS_REMOUNT is forbidden by the kernel.
-        WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
-        return false;
-    }
-    if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
-        WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
-        return false;
-    }
-    return true;
-}
-
-static void reboot_for_remount(int fd, bool need_fsck) {
-    std::string reboot_cmd = "reboot";
-    if (need_fsck) {
-        const std::vector<std::string> options = {"--fsck_unshare_blocks"};
-        std::string err;
-        if (!write_bootloader_message(options, &err)) {
-            WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
-            return;
-        }
-
-        WriteFdExactly(fd,
-                       "The device will now reboot to recovery and attempt "
-                       "un-deduplication.\n");
-        reboot_cmd = "reboot,recovery";
-    }
-
-    sync();
-    android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
-}
-
-static void try_unmount_bionic(int fd) {
-    static constexpr const char* kBionic = "/bionic";
-    struct statfs buf;
-    if (statfs(kBionic, &buf) == -1) {
-        WriteFdFmt(fd, "statfs of the %s mount failed: %s.\n", kBionic, strerror(errno));
-        return;
-    }
-    if (buf.f_flags & ST_RDONLY) {
-        // /bionic is on a read-only partition; can happen for
-        // non-system-as-root-devices. Don' try to unmount.
-        return;
-    }
-    // Success/Fail of the actual remount will be reported by the function.
-    remount_partition(fd, kBionic);
-    return;
-}
-
 void remount_service(unique_fd fd, const std::string& cmd) {
-    bool user_requested_reboot = cmd == "-R";
-
-    if (getuid() != 0) {
-        WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
-        return;
-    }
-
-    bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
-    bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
-
-    std::vector<std::string> partitions{"/",        "/odm",   "/oem", "/product_services",
-                                        "/product", "/vendor"};
-
-    if (system_verified || vendor_verified) {
-        // Disable verity automatically (reboot will be required).
-        set_verity_enabled_state_service(unique_fd(dup(fd.get())), false);
-
-        // If overlayfs is not supported, we try and remount or set up
-        // un-deduplication. If it is supported, we can go ahead and wait for
-        // a reboot.
-        if (fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported) {
-            if (user_requested_reboot) {
-                if (android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot")) {
-                    WriteFdExactly(fd.get(), "rebooting device\n");
-                } else {
-                    WriteFdExactly(fd.get(), "reboot failed\n");
-                }
-            }
-            return;
-        }
-    } else if (fs_mgr_overlayfs_setup()) {
-        // If we can use overlayfs, lets get it in place first before we
-        // struggle with determining deduplication operations.
-        Fstab fstab;
-        if (ReadDefaultFstab(&fstab) && fs_mgr_overlayfs_mount_all(&fstab)) {
-            WriteFdExactly(fd.get(), "overlayfs mounted\n");
-        }
-    }
-
-    // If overlayfs is supported, we don't bother trying to un-deduplicate
-    // partitions.
-    std::set<std::string> dedup;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
-        // Find partitions that are deduplicated, and can be un-deduplicated.
-        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;
-            }
-            if (can_unshare_blocks(fd.get(), dev.c_str())) {
-                dedup.emplace(partition);
-            }
-        }
-
-        // Reboot now if the user requested it (and an operation needs a reboot).
-        if (user_requested_reboot) {
-            if (!dedup.empty()) {
-                reboot_for_remount(fd.get(), !dedup.empty());
-                return;
-            }
-            WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
-        }
-    }
-
-    bool success = true;
-    for (const auto& partition : partitions) {
-        // Don't try to remount partitions that need an fsck in recovery.
-        if (dedup.count(partition)) {
-            continue;
-        }
-        success &= remount_partition(fd.get(), partition.c_str());
-    }
-
-    if (!dedup.empty()) {
-        WriteFdExactly(fd.get(),
-                       "The following partitions are deduplicated and cannot "
-                       "yet be remounted:\n");
-        for (const std::string& name : dedup) {
-            WriteFdFmt(fd.get(), "  %s\n", name.c_str());
-        }
-
-        WriteFdExactly(fd.get(),
-                       "To reboot and un-deduplicate the listed partitions, "
-                       "please retry with adb remount -R.\n");
-        if (system_verified || vendor_verified) {
-            WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
-        }
-        return;
-    }
-
-    try_unmount_bionic(fd.get());
-
-    if (!success) {
-        WriteFdExactly(fd.get(), "remount failed\n");
-    } else {
-        WriteFdExactly(fd.get(), "remount succeeded\n");
-    }
+    const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
+    WriteFdFmt(fd.get(), "remount %s\n", success);
 }
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
index c847403..522a5da 100644
--- a/adb/daemon/remount_service.h
+++ b/adb/daemon/remount_service.h
@@ -21,6 +21,5 @@
 #include "adb_unique_fd.h"
 
 #if defined(__ANDROID__)
-bool make_block_device_writable(const std::string&);
 void remount_service(unique_fd, const std::string&);
 #endif
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 92851c0..889229f 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -25,6 +25,7 @@
 #include <libavb_user/libavb_user.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 
 #include <android-base/properties.h>
@@ -37,7 +38,6 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
-#include "remount_service.h"
 
 #include "fec/io.h"
 
@@ -51,6 +51,17 @@
     if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
 }
 
+static bool make_block_device_writable(const std::string& dev) {
+    unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
+    if (fd == -1) {
+        return false;
+    }
+
+    int OFF = 0;
+    bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
+    return result;
+}
+
 /* Turn verity on/off */
 static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
                                      bool enable) {
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 8798ad3..c08afda 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -316,7 +316,7 @@
   std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
 
   std::string line;
-  for (unwindstack::MapInfo* map_info : *maps) {
+  for (auto const& map_info : *maps) {
     line = "    ";
     if (print_fault_address_marker) {
       if (addr < map_info->start) {
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index c0e0ccd..5c4008c 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -42,13 +42,14 @@
 
 [[noreturn]] void usage(int exit_status) {
     LOG(INFO) << getprogname()
-              << " [-h] [-R] [-T fstab_file]\n"
+              << " [-h] [-R] [-T fstab_file] [partition]...\n"
                  "\t-h --help\tthis help\n"
                  "\t-R --reboot\tdisable verity & reboot to facilitate remount\n"
                  "\t-T --fstab\tcustom fstab file location\n"
+                 "\tpartition\tspecific partition(s) (empty does all)\n"
                  "\n"
-                 "Remount all partitions read-write.\n"
-                 "-R notwithstanding, verity must be disabled.";
+                 "Remount specified partition(s) read-write, by name or mount point.\n"
+                 "-R notwithstanding, verity must be disabled on partition(s).";
 
     ::exit(exit_status);
 }
@@ -138,6 +139,8 @@
         BADARG,
         NOT_ROOT,
         NO_FSTAB,
+        UNKNOWN_PARTITION,
+        INVALID_PARTITION,
         VERITY_PARTITION,
         BAD_OVERLAY,
         NO_MOUNTS,
@@ -183,11 +186,6 @@
         }
     }
 
-    if (argc > optind) {
-        LOG(ERROR) << "Bad Argument " << argv[optind];
-        usage(BADARG);
-    }
-
     // Make sure we are root.
     if (::getuid() != 0) {
         LOG(ERROR) << "must be run as root";
@@ -211,47 +209,97 @@
     auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
 
     // Generate the all remountable partitions sub-list
-    android::fs_mgr::Fstab partitions;
+    android::fs_mgr::Fstab all;
     for (auto const& entry : fstab) {
         if (!remountable_partition(entry)) continue;
         if (overlayfs_candidates.empty() ||
             GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
             (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
-            partitions.emplace_back(entry);
+            all.emplace_back(entry);
         }
     }
 
+    // Parse the unique list of valid partition arguments.
+    android::fs_mgr::Fstab partitions;
+    for (; argc > optind; ++optind) {
+        auto partition = std::string(argv[optind]);
+        if (partition.empty()) continue;
+        if (partition == "/") partition = "/system";
+        auto find_part = [&partition](const auto& entry) {
+            const auto mount_point = system_mount_point(entry);
+            if (partition == mount_point) return true;
+            if (partition == android::base::Basename(mount_point)) return true;
+            return false;
+        };
+        // Do we know about the partition?
+        auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
+        if (it == fstab.end()) {
+            LOG(ERROR) << "Unknown partition " << partition << ", skipping";
+            retval = UNKNOWN_PARTITION;
+            continue;
+        }
+        // Is that one covered by an existing overlayfs?
+        auto wrap = is_wrapped(overlayfs_candidates, *it);
+        if (wrap) {
+            LOG(INFO) << "partition " << partition << " covered by overlayfs for "
+                      << wrap->mount_point << ", switching";
+            partition = system_mount_point(*wrap);
+        }
+        // Is it a remountable partition?
+        it = std::find_if(all.begin(), all.end(), find_part);
+        if (it == all.end()) {
+            LOG(ERROR) << "Invalid partition " << partition << ", skipping";
+            retval = INVALID_PARTITION;
+            continue;
+        }
+        if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
+            partitions.emplace_back(*it);
+        }
+    }
+
+    if (partitions.empty() && !retval) {
+        partitions = all;
+    }
+
     // Check verity and optionally setup overlayfs backing.
     auto reboot_later = false;
+    auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
     for (auto it = partitions.begin(); it != partitions.end();) {
         auto& entry = *it;
         auto& mount_point = entry.mount_point;
         if (fs_mgr_is_verity_enabled(entry)) {
-            LOG(WARNING) << "Verity enabled on " << mount_point;
-            if (can_reboot &&
-                (android::base::GetProperty("ro.boot.vbmeta.devices_state", "") != "locked")) {
+            retval = VERITY_PARTITION;
+            if (android::base::GetProperty("ro.boot.vbmeta.devices_state", "") != "locked") {
                 if (AvbOps* ops = avb_ops_user_new()) {
                     auto ret = avb_user_verity_set(
                             ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
                             false);
                     avb_ops_user_free(ops);
                     if (ret) {
-                        if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
-                            retval = VERITY_PARTITION;
+                        LOG(WARNING) << "Disable verity for " << mount_point;
+                        reboot_later = can_reboot;
+                        if (reboot_later) {
                             // w/o overlayfs available, also check for dedupe
-                            reboot_later = true;
-                            ++it;
-                            continue;
+                            if (!uses_overlayfs) {
+                                ++it;
+                                continue;
+                            }
+                            reboot(false);
                         }
-                        reboot(false);
                     } else if (fs_mgr_set_blk_ro(entry.blk_device, false)) {
                         fec::io fh(entry.blk_device.c_str(), O_RDWR);
-                        if (fh && fh.set_verity_status(false)) reboot_later = true;
+                        if (fh && fh.set_verity_status(false)) {
+                            LOG(WARNING) << "Disable verity for " << mount_point;
+                            reboot_later = can_reboot;
+                            if (reboot_later && !uses_overlayfs) {
+                                ++it;
+                                continue;
+                            }
+                        }
                     }
                 }
             }
             LOG(ERROR) << "Skipping " << mount_point;
-            retval = VERITY_PARTITION;
             it = partitions.erase(it);
             continue;
         }
@@ -278,7 +326,8 @@
     }
 
     // Mount overlayfs.
-    if (!fs_mgr_overlayfs_mount_all(&partitions)) {
+    errno = 0;
+    if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
         retval = BAD_OVERLAY;
         PLOG(ERROR) << "Can not mount overlayfs for partitions";
     }
@@ -306,11 +355,15 @@
                 break;
             }
             if ((mount_point == "/") && (rentry.mount_point == "/system")) {
-                if (blk_device != "/dev/root") blk_device = rentry.blk_device;
+                blk_device = rentry.blk_device;
                 mount_point = "/system";
                 break;
             }
         }
+        if (blk_device == "/dev/root") {
+            auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+            if (from_fstab) blk_device = from_fstab->blk_device;
+        }
         fs_mgr_set_blk_ro(blk_device, false);
 
         // Now remount!
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
index 33c3cad..7463810 100644
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -20,13 +20,17 @@
     recovery_available: true,
     export_include_dirs: ["include"],
     cflags: [
-        // TODO(b/121211685): Allows us to create a skeleton of required classes
-        "-Wno-unused-private-field",
-        "-Wno-unused-parameter",
+        "-D_FILE_OFFSET_BITS=64",
     ],
 
     srcs: [
         "fiemap_writer.cpp",
+        "split_fiemap_writer.cpp",
+        "utility.cpp",
+    ],
+
+    static_libs: [
+        "libext4_utils",
     ],
 
     header_libs: [
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 8b880e6..3d41876 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -27,6 +27,7 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <limits>
 #include <string>
 #include <utility>
 #include <vector>
@@ -206,10 +207,15 @@
     }
 
     // Check if the filesystem is of supported types.
-    // Only ext4 and f2fs are tested and supported.
-    if ((sfs.f_type != EXT4_SUPER_MAGIC) && (sfs.f_type != F2FS_SUPER_MAGIC)) {
-        LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
-        return false;
+    // Only ext4, f2fs, and vfat are tested and supported.
+    switch (sfs.f_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+        case MSDOS_SUPER_MAGIC:
+            break;
+        default:
+            LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
+            return false;
     }
 
     uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
@@ -224,14 +230,46 @@
 }
 
 static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
-                         uint64_t file_size, std::function<bool(uint64_t, uint64_t)> on_progress) {
+                         uint64_t file_size, unsigned int fs_type,
+                         std::function<bool(uint64_t, uint64_t)> on_progress) {
     // Reserve space for the file on the file system and write it out to make sure the extents
     // don't come back unwritten. Return from this function with the kernel file offset set to 0.
     // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
     // aren't moved around.
-    if (fallocate64(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
-        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
-        return false;
+    switch (fs_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+            if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+                PLOG(ERROR) << "Failed to allocate space for file: " << file_path
+                            << " size: " << file_size;
+                return false;
+            }
+            break;
+        case MSDOS_SUPER_MAGIC: {
+            // fallocate() is not supported, and not needed, since VFAT does not support holes.
+            // Instead we can perform a much faster allocation.
+            auto offset = TEMP_FAILURE_RETRY(lseek(file_fd, file_size - 1, SEEK_SET));
+            if (offset < 0) {
+                PLOG(ERROR) << "Failed to lseek " << file_path;
+                return false;
+            }
+            if (offset != file_size - 1) {
+                LOG(ERROR) << "Seek returned wrong offset " << offset << " for file " << file_path;
+                return false;
+            }
+            char buffer[] = {0};
+            if (!android::base::WriteFully(file_fd, buffer, 1)) {
+                PLOG(ERROR) << "Write failed: " << file_path;
+                return false;
+            }
+            if (on_progress && !on_progress(file_size, file_size)) {
+                return false;
+            }
+            return true;
+        }
+        default:
+            LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
+            return false;
     }
 
     // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
@@ -286,9 +324,9 @@
 }
 
 static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type == EXT4_SUPER_MAGIC) {
-        // No pinning necessary for ext4. The blocks, once allocated, are expected
-        // to be fixed.
+    if (fs_type != F2FS_SUPER_MAGIC) {
+        // No pinning necessary for ext4/msdos. The blocks, once allocated, are
+        // expected to be fixed.
         return true;
     }
 
@@ -323,9 +361,9 @@
 }
 
 static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type == EXT4_SUPER_MAGIC) {
-        // No pinning necessary for ext4. The blocks, once allocated, are expected
-        // to be fixed.
+    if (fs_type != F2FS_SUPER_MAGIC) {
+        // No pinning necessary for ext4 or vfat. The blocks, once allocated,
+        // are expected to be fixed.
         return true;
     }
 
@@ -437,6 +475,44 @@
     return last_extent_seen;
 }
 
+static bool ReadFibmap(int file_fd, const std::string& file_path,
+                       std::vector<struct fiemap_extent>* extents) {
+    struct stat s;
+    if (fstat(file_fd, &s)) {
+        PLOG(ERROR) << "Failed to stat " << file_path;
+        return false;
+    }
+
+    uint64_t num_blocks = (s.st_size + s.st_blksize - 1) / s.st_blksize;
+    if (num_blocks > std::numeric_limits<uint32_t>::max()) {
+        LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
+        return false;
+    }
+
+    for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
+        uint32_t block = block_number;
+        if (ioctl(file_fd, FIBMAP, &block)) {
+            PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
+            return false;
+        }
+        if (!block) {
+            LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
+            return false;
+        }
+
+        if (!extents->empty() && block == last_block + 1) {
+            extents->back().fe_length++;
+        } else {
+            extents->push_back(fiemap_extent{.fe_logical = block_number,
+                                             .fe_physical = block,
+                                             .fe_length = 1,
+                                             .fe_flags = 0});
+        }
+        last_block = block;
+    }
+    return true;
+}
+
 FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
                                    std::function<bool(uint64_t, uint64_t)> progress) {
     // if 'create' is false, open an existing file and do not truncate.
@@ -505,7 +581,7 @@
     }
 
     if (create) {
-        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, std::move(progress))) {
+        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
             LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
                        << " bytes";
             cleanup(abs_path, create);
@@ -522,10 +598,22 @@
 
     // now allocate the FiemapWriter and start setting it up
     FiemapUniquePtr fmap(new FiemapWriter());
-    if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
-        LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
-        cleanup(abs_path, create);
-        return nullptr;
+    switch (fs_type) {
+        case EXT4_SUPER_MAGIC:
+        case F2FS_SUPER_MAGIC:
+            if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
+                LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
+                cleanup(abs_path, create);
+                return nullptr;
+            }
+            break;
+        case MSDOS_SUPER_MAGIC:
+            if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
+                LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
+                cleanup(abs_path, create);
+                return nullptr;
+            }
+            break;
     }
 
     fmap->file_path_ = abs_path;
@@ -536,14 +624,10 @@
     fmap->fs_type_ = fs_type;
     fmap->block_size_ = blocksz;
 
-    LOG(INFO) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
-              << bdev_path;
+    LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
+                 << bdev_path;
     return fmap;
 }
 
-bool FiemapWriter::Read(off64_t off, uint8_t* buffer, uint64_t size) {
-    return false;
-}
-
 }  // namespace fiemap_writer
 }  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index d1c0aad..ab4efae 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -33,8 +33,10 @@
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <libdm/loop_control.h>
-
 #include <libfiemap_writer/fiemap_writer.h>
+#include <libfiemap_writer/split_fiemap_writer.h>
+
+#include "utility.h"
 
 using namespace std;
 using namespace std::string_literals;
@@ -59,6 +61,24 @@
     std::string testfile;
 };
 
+class SplitFiemapTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        testfile = gTestDir + "/"s + tinfo->name();
+    }
+
+    void TearDown() override {
+        std::string message;
+        if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
+            cerr << "Could not remove all split files: " << message;
+        }
+    }
+
+    // name of the file we use for testing
+    std::string testfile;
+};
+
 TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
     // Try creating a file of size ~100TB but aligned to
     // 512 byte to make sure block alignment tests don't
@@ -71,8 +91,7 @@
 
 TEST_F(FiemapWriterTest, CreateUnalignedFile) {
     // Try creating a file of size 4097 bytes which is guaranteed
-    // to be unaligned to all known block sizes. The creation must
-    // fail.
+    // to be unaligned to all known block sizes.
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
     ASSERT_NE(fptr, nullptr);
     ASSERT_EQ(fptr->size(), gBlockSize * 2);
@@ -87,10 +106,7 @@
 }
 
 TEST_F(FiemapWriterTest, CheckProgress) {
-    std::vector<uint64_t> expected{
-            0,
-            gBlockSize,
-    };
+    std::vector<uint64_t> expected;
     size_t invocations = 0;
     auto callback = [&](uint64_t done, uint64_t total) -> bool {
         EXPECT_LT(invocations, expected.size());
@@ -100,9 +116,22 @@
         return true;
     };
 
+    uint32_t fs_type;
+    {
+        auto ptr = FiemapWriter::Open(testfile, gBlockSize, true);
+        ASSERT_NE(ptr, nullptr);
+        fs_type = ptr->fs_type();
+    }
+    ASSERT_EQ(unlink(testfile.c_str()), 0);
+
+    if (fs_type != MSDOS_SUPER_MAGIC) {
+        expected.push_back(0);
+    }
+    expected.push_back(gBlockSize);
+
     auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
     EXPECT_NE(ptr, nullptr);
-    EXPECT_EQ(invocations, 2);
+    EXPECT_EQ(invocations, expected.size());
 }
 
 TEST_F(FiemapWriterTest, CheckPinning) {
@@ -159,6 +188,52 @@
     EXPECT_EQ(errno, ENOENT);
 }
 
+TEST_F(FiemapWriterTest, MaxBlockSize) {
+    ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
+}
+
+TEST_F(SplitFiemapTest, Create) {
+    auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+    ASSERT_NE(ptr, nullptr);
+
+    auto extents = ptr->extents();
+
+    // Destroy the fiemap, closing file handles. This should not delete them.
+    ptr = nullptr;
+
+    std::vector<std::string> files;
+    ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
+    for (const auto& path : files) {
+        EXPECT_EQ(access(path.c_str(), F_OK), 0);
+    }
+
+    ASSERT_GE(extents.size(), files.size());
+}
+
+TEST_F(SplitFiemapTest, Open) {
+    {
+        auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
+        ASSERT_NE(ptr, nullptr);
+    }
+
+    auto ptr = SplitFiemap::Open(testfile);
+    ASSERT_NE(ptr, nullptr);
+
+    auto extents = ptr->extents();
+    ASSERT_GE(extents.size(), 24);
+}
+
+TEST_F(SplitFiemapTest, DeleteOnFail) {
+    auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 10, 1);
+    ASSERT_EQ(ptr, nullptr);
+
+    std::string first_file = testfile + ".0001";
+    ASSERT_NE(access(first_file.c_str(), F_OK), 0);
+    ASSERT_EQ(errno, ENOENT);
+    ASSERT_NE(access(testfile.c_str(), F_OK), 0);
+    ASSERT_EQ(errno, ENOENT);
+}
+
 class VerifyBlockWritesExt4 : public ::testing::Test {
     // 2GB Filesystem and 4k block size by default
     static constexpr uint64_t block_size = 4096;
@@ -273,7 +348,10 @@
         cerr << "unable to create tempdir on " << argv[1] << "\n";
         exit(EXIT_FAILURE);
     }
-    gTestDir = tempdir;
+    if (!android::base::Realpath(tempdir, &gTestDir)) {
+        cerr << "unable to find realpath for " << tempdir;
+        exit(EXIT_FAILURE);
+    }
 
     if (argc > 2) {
         testfile_size = strtoull(argv[2], NULL, 0);
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
index 3ba68e9..831bc75 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -67,11 +67,6 @@
     static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
                                       bool* uses_dm = nullptr);
 
-    // The counter part of Write(). It is an error for the offset to be unaligned with
-    // the block device's block size.
-    // In case of error, the contents of buffer MUST be discarded.
-    bool Read(off64_t off, uint8_t* buffer, uint64_t size);
-
     ~FiemapWriter() = default;
 
     const std::string& file_path() const { return file_path_; };
@@ -79,6 +74,7 @@
     const std::string& bdev_path() const { return bdev_path_; };
     uint64_t block_size() const { return block_size_; };
     const std::vector<struct fiemap_extent>& extents() { return extents_; };
+    uint32_t fs_type() const { return fs_type_; }
 
     // Non-copyable & Non-movable
     FiemapWriter(const FiemapWriter&) = delete;
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
new file mode 100644
index 0000000..765cc84
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "fiemap_writer.h"
+
+namespace android {
+namespace fiemap_writer {
+
+// Wrapper around FiemapWriter that is able to split images across files if
+// necessary.
+class SplitFiemap final {
+  public:
+    using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
+
+    // Create a new split fiemap file. If |max_piece_size| is 0, the number of
+    // pieces will be determined automatically by detecting the filesystem.
+    // Otherwise, the file will be split evenly (with the remainder in the
+    // final file).
+    static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
+                                               uint64_t max_piece_size,
+                                               ProgressCallback progress = {});
+
+    // Open an existing split fiemap file.
+    static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
+
+    ~SplitFiemap();
+
+    // Return a list of all files created for a split file.
+    static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
+
+    // Destroy all components of a split file. If the root file does not exist,
+    // this returns true and does not report an error.
+    static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
+
+    const std::vector<struct fiemap_extent>& extents();
+    uint32_t block_size() const;
+    uint64_t size() const { return total_size_; }
+
+    // Non-copyable & Non-movable
+    SplitFiemap(const SplitFiemap&) = delete;
+    SplitFiemap& operator=(const SplitFiemap&) = delete;
+    SplitFiemap& operator=(SplitFiemap&&) = delete;
+    SplitFiemap(SplitFiemap&&) = delete;
+
+  private:
+    SplitFiemap() = default;
+    void AddFile(FiemapUniquePtr&& file);
+
+    bool creating_ = false;
+    std::string list_file_;
+    std::vector<FiemapUniquePtr> files_;
+    std::vector<struct fiemap_extent> extents_;
+    uint64_t total_size_ = 0;
+};
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
new file mode 100644
index 0000000..1f80370
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libfiemap_writer/split_fiemap_writer.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fiemap_writer {
+
+using android::base::unique_fd;
+
+// We use a four-digit suffix at the end of filenames.
+static const size_t kMaxFilePieces = 500;
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
+                                                 uint64_t max_piece_size,
+                                                 ProgressCallback progress) {
+    if (!file_size) {
+        LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
+        return nullptr;
+    }
+
+    if (!max_piece_size) {
+        max_piece_size = DetermineMaximumFileSize(file_path);
+        if (!max_piece_size) {
+            LOG(ERROR) << "Could not determine maximum file size for " << file_path;
+            return nullptr;
+        }
+    }
+
+    // Call |progress| only when the total percentage would significantly change.
+    int permille = -1;
+    uint64_t total_bytes_written = 0;
+    auto on_progress = [&](uint64_t written, uint64_t) -> bool {
+        uint64_t actual_written = total_bytes_written + written;
+        int new_permille = (actual_written * 1000) / file_size;
+        if (new_permille != permille && actual_written < file_size) {
+            if (progress && !progress(actual_written, file_size)) {
+                return false;
+            }
+            permille = new_permille;
+        }
+        return true;
+    };
+
+    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+    out->creating_ = true;
+    out->list_file_ = file_path;
+
+    // Create the split files.
+    uint64_t remaining_bytes = file_size;
+    while (remaining_bytes) {
+        if (out->files_.size() >= kMaxFilePieces) {
+            LOG(ERROR) << "Requested size " << file_size << " created too many split files";
+            return nullptr;
+        }
+        std::string chunk_path =
+                android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
+        uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
+        auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
+        if (!writer) {
+            return nullptr;
+        }
+
+        // To make sure the alignment doesn't create too much inconsistency, we
+        // account the *actual* size, not the requested size.
+        total_bytes_written += writer->size();
+        remaining_bytes -= writer->size();
+
+        out->AddFile(std::move(writer));
+    }
+
+    // Create the split file list.
+    unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open " << file_path;
+        return nullptr;
+    }
+
+    for (const auto& writer : out->files_) {
+        std::string line = android::base::Basename(writer->file_path()) + "\n";
+        if (!android::base::WriteFully(fd, line.data(), line.size())) {
+            PLOG(ERROR) << "Write failed " << file_path;
+            return nullptr;
+        }
+    }
+
+    // Unset this bit, so we don't unlink on destruction.
+    out->creating_ = false;
+    return out;
+}
+
+std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
+    std::vector<std::string> files;
+    if (!GetSplitFileList(file_path, &files)) {
+        return nullptr;
+    }
+
+    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
+    out->list_file_ = file_path;
+
+    for (const auto& file : files) {
+        auto writer = FiemapWriter::Open(file, 0, false);
+        if (!writer) {
+            // Error was logged in Open().
+            return nullptr;
+        }
+        out->AddFile(std::move(writer));
+    }
+    return out;
+}
+
+bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
+    // This is not the most efficient thing, but it is simple and recovering
+    // the fiemap/fibmap is much more expensive.
+    std::string contents;
+    if (!android::base::ReadFileToString(file_path, &contents, true)) {
+        PLOG(ERROR) << "Error reading file: " << file_path;
+        return false;
+    }
+
+    std::vector<std::string> names = android::base::Split(contents, "\n");
+    std::string dir = android::base::Dirname(file_path);
+    for (const auto& name : names) {
+        if (!name.empty()) {
+            list->emplace_back(dir + "/" + name);
+        }
+    }
+    return true;
+}
+
+bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
+    // Early exit if this does not exist, and do not report an error.
+    if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
+        return true;
+    }
+
+    bool ok = true;
+    std::vector<std::string> files;
+    if (GetSplitFileList(file_path, &files)) {
+        for (const auto& file : files) {
+            ok &= android::base::RemoveFileIfExists(file, message);
+        }
+    }
+    ok &= android::base::RemoveFileIfExists(file_path, message);
+    return ok;
+}
+
+const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
+    if (extents_.empty()) {
+        for (const auto& file : files_) {
+            const auto& extents = file->extents();
+            extents_.insert(extents_.end(), extents.begin(), extents.end());
+        }
+    }
+    return extents_;
+}
+
+SplitFiemap::~SplitFiemap() {
+    if (!creating_) {
+        return;
+    }
+
+    // We failed to finish creating, so unlink everything.
+    unlink(list_file_.c_str());
+    for (auto&& file : files_) {
+        std::string path = file->file_path();
+        file = nullptr;
+
+        unlink(path.c_str());
+    }
+}
+
+void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
+    total_size_ += file->size();
+    files_.emplace_back(std::move(file));
+}
+
+uint32_t SplitFiemap::block_size() const {
+    return files_[0]->block_size();
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.cpp b/fs_mgr/libfiemap_writer/utility.cpp
new file mode 100644
index 0000000..192ec16
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utility.h"
+
+#include <stdint.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <libfiemap_writer/fiemap_writer.h>
+
+namespace android {
+namespace fiemap_writer {
+
+uint64_t DetermineMaximumFileSize(const std::string& file_path) {
+    // Create the smallest file possible (one block).
+    auto writer = FiemapWriter::Open(file_path, 1);
+    if (!writer) {
+        return 0;
+    }
+
+    uint64_t result = 0;
+    switch (writer->fs_type()) {
+        case EXT4_SUPER_MAGIC:
+            // The minimum is 16GiB, so just report that. If we wanted we could parse the
+            // superblock and figure out if 64-bit support is enabled.
+            result = 17179869184ULL;
+            break;
+        case F2FS_SUPER_MAGIC:
+            // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
+            // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
+            result = 4329690886144ULL;
+            break;
+        case MSDOS_SUPER_MAGIC:
+            // 4GB-1, which we want aligned to the block size.
+            result = 4294967295;
+            result -= (result % writer->block_size());
+            break;
+        default:
+            LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
+            break;
+    }
+
+    // Close and delete the temporary file.
+    writer = nullptr;
+    unlink(file_path.c_str());
+
+    return result;
+}
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.h b/fs_mgr/libfiemap_writer/utility.h
new file mode 100644
index 0000000..2d418da
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/utility.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace fiemap_writer {
+
+// Given a file that will be created, determine the maximum size its containing
+// filesystem allows. Note this is a theoretical maximum size; free space is
+// ignored entirely.
+uint64_t DetermineMaximumFileSize(const std::string& file_path);
+
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 1ded954..bd5a4fe 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -48,6 +48,7 @@
 TMPDIR=${TMPDIR:-/tmp}
 print_time=false
 start_time=`date +%s`
+ACTIVE_SLOT=
 
 ##
 ##  Helper Functions
@@ -77,6 +78,7 @@
       wc -l | grep '^1$' >/dev/null
     fi
 }
+
 [ "USAGE: inRecovery
 
 Returns: true if device is in recovery mode" ]
@@ -221,15 +223,23 @@
 
 Returns: waits until the device has returned for adb or optional timeout" ]
 adb_wait() {
+  local ret
   if [ -n "${1}" ]; then
     echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
     timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
-    local ret=${?}
+    ret=${?}
     echo -n "                                                                             ${CR}"
-    return ${ret}
   else
     adb wait-for-device
+    ret=${?}
   fi
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
+  fi
+  return ${ret}
 }
 
 [ "USAGE: usb_status > stdout
@@ -254,33 +264,50 @@
 
 Returns: waits until the device has returned for fastboot or optional timeout" ]
 fastboot_wait() {
+  local ret
   # fastboot has no wait-for-device, but it does an automatic
   # wait and requires (even a nonsensical) command to do so.
   if [ -n "${1}" ]; then
     echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
     timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
-    local ret=${?}
+    ret=${?}
     echo -n "                                                                             ${CR}"
     ( exit ${ret} )
   else
     fastboot wait-for-device >/dev/null 2>/dev/null
   fi ||
     inFastboot
+  ret=${?}
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
+  fi
+  return ${ret}
 }
 
 [ "USAGE: recovery_wait [timeout]
 
 Returns: waits until the device has returned for recovery or optional timeout" ]
 recovery_wait() {
+  local ret
   if [ -n "${1}" ]; then
     echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
     timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
-    local ret=${?}
+    ret=${?}
     echo -n "                                                                             ${CR}"
-    return ${ret}
   else
     adb wait-for-recovery
+    ret=${?}
   fi
+  if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+    local active_slot=`get_active_slot`
+    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
+  fi
+  return ${ret}
 }
 
 [ "any_wait [timeout]
@@ -326,7 +353,7 @@
     [ root != "`adb_sh echo '${USER}' </dev/null`" ]
 }
 
-[ "USAGE: fastboot_getvar var expected
+[ "USAGE: fastboot_getvar var expected >/dev/stderr
 
 Returns: true if var output matches expected" ]
 fastboot_getvar() {
@@ -350,6 +377,19 @@
   echo ${O} >&2
 }
 
+[ "USAGE: get_active_slot >/dev/stdout
+
+Returns: with a or b string reporting active slot" ]
+get_active_slot() {
+  if inAdb || inRecovery; then
+    get_property ro.boot.slot_suffix | tr -d _
+  elif inFastboot; then
+    fastboot_getvar current-slot 2>&1 | sed -n 's/current-slot: //p'
+  else
+    false
+  fi
+}
+
 [ "USAGE: restore
 
 Do nothing: should be redefined when necessary.  Called after cleanup.
@@ -583,6 +623,9 @@
 BUILD_DESCRIPTION=`get_property ro.build.description`
 [ -z "${BUILD_DESCRIPTION}" ] ||
   echo "${BLUE}[     INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+ACTIVE_SLOT=`get_active_slot`
+[ -z "${ACTIVE_SLOT}" ] ||
+  echo "${BLUE}[     INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
 
 # Report existing partition sizes
 adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
@@ -620,7 +663,7 @@
 
   echo "${GREEN}[ RUN      ]${NORMAL} Testing adb shell su root remount -R command" >&2
 
-  adb_su remount -R </dev/null || true
+  adb_su remount -R system </dev/null || true
   sleep 2
   adb_wait 2m ||
     die "waiting for device after remount -R `usb_status`"
@@ -1031,13 +1074,7 @@
   check_eq "${A}" "${B}" system after flash vendor
   adb_root ||
     die "adb root"
-  B="`adb_cat /vendor/hello`" &&
-    if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
-      die "re-read /vendor/hello after flash vendor"
-    else
-      echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
-      echo "${ORANGE}[  WARNING ]${NORMAL} re-read /vendor/hello after flash vendor" >&2
-    fi
+  B="`adb_cat /vendor/hello`"
   if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
     check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
              vendor content after flash vendor
@@ -1159,10 +1196,12 @@
   die "lost device after reboot to ro state (USB stack broken?)"
 adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
   die "/vendor is not read-only"
-adb_su remount </dev/null ||
+adb_su remount vendor </dev/null ||
   die "remount command"
 adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
   die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/vendor is not read-only"
 echo "${GREEN}[       OK ]${NORMAL} remount command works from setup" >&2
 
 # Prerequisite is an overlayfs deconstructed device but with verity disabled.
@@ -1177,10 +1216,12 @@
   die "lost device after reboot after wipe (USB stack broken?)"
 adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
   die "/vendor is not read-only"
-adb_su remount </dev/null ||
+adb_su remount vendor </dev/null ||
   die "remount command"
 adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
   die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+  die "/system is not read-only"
 echo "${GREEN}[       OK ]${NORMAL} remount command works from scratch" >&2
 
 restore
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 8e9c7ea..2b7db79 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -31,6 +31,7 @@
     shared_libs: [
         "libbinder",
         "libgatekeeper",
+        "libgsi",
         "liblog",
         "libhardware",
         "libbase",
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 446b66e..8700c34 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -26,6 +26,8 @@
 #include <memory>
 
 #include <android/security/keystore/IKeystoreService.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
@@ -34,6 +36,7 @@
 #include <hardware/hw_auth_token.h>
 #include <keystore/keystore.h> // For error code
 #include <keystore/keystore_return_types.h>
+#include <libgsi/libgsi.h>
 #include <log/log.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
@@ -59,6 +62,7 @@
     GateKeeperProxy() {
         clear_state_if_needed_done = false;
         hw_device = IGatekeeper::getService();
+        is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
 
         if (hw_device == nullptr) {
             ALOGW("falling back to software GateKeeper");
@@ -86,7 +90,7 @@
             return;
         }
 
-        if (mark_cold_boot()) {
+        if (mark_cold_boot() && !is_running_gsi) {
             ALOGI("cold boot: clearing state");
             if (hw_device != nullptr) {
                 hw_device->deleteAllUsers([](const GatekeeperResponse &){});
@@ -138,6 +142,18 @@
         }
     }
 
+    // This should only be called on uids being passed to the GateKeeper HAL. It ensures that
+    // secure storage shared across a GSI image and a host image will not overlap.
+    uint32_t adjust_uid(uint32_t uid) {
+        static constexpr uint32_t kGsiOffset = 1000000;
+        CHECK(uid < kGsiOffset);
+        CHECK(hw_device != nullptr);
+        if (is_running_gsi) {
+            return uid + kGsiOffset;
+        }
+        return uid;
+    }
+
     virtual int enroll(uint32_t uid,
             const uint8_t *current_password_handle, uint32_t current_password_handle_length,
             const uint8_t *current_password, uint32_t current_password_length,
@@ -181,7 +197,8 @@
             newPwd.setToExternal(const_cast<uint8_t*>(desired_password),
                                  desired_password_length);
 
-            Return<void> hwRes = hw_device->enroll(uid, curPwdHandle, curPwd, newPwd,
+            uint32_t hw_uid = adjust_uid(uid);
+            Return<void> hwRes = hw_device->enroll(hw_uid, curPwdHandle, curPwd, newPwd,
                               [&ret, enrolled_password_handle, enrolled_password_handle_length]
                                    (const GatekeeperResponse &rsp) {
                 ret = static_cast<int>(rsp.code); // propagate errors
@@ -266,13 +283,14 @@
             // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
             // a HAL if there was none before
             if (handle->version == 0 || handle->hardware_backed) {
+                uint32_t hw_uid = adjust_uid(uid);
                 android::hardware::hidl_vec<uint8_t> curPwdHandle;
                 curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolled_password_handle),
                                            enrolled_password_handle_length);
                 android::hardware::hidl_vec<uint8_t> enteredPwd;
                 enteredPwd.setToExternal(const_cast<uint8_t*>(provided_password),
                                          provided_password_length);
-                Return<void> hwRes = hw_device->verify(uid, challenge, curPwdHandle, enteredPwd,
+                Return<void> hwRes = hw_device->verify(hw_uid, challenge, curPwdHandle, enteredPwd,
                                         [&ret, request_reenroll, auth_token, auth_token_length]
                                              (const GatekeeperResponse &rsp) {
                     ret = static_cast<int>(rsp.code); // propagate errors
@@ -354,7 +372,8 @@
         clear_sid(uid);
 
         if (hw_device != nullptr) {
-            hw_device->deleteUser(uid, [] (const GatekeeperResponse &){});
+            uint32_t hw_uid = adjust_uid(uid);
+            hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
         }
     }
 
@@ -394,6 +413,7 @@
     std::unique_ptr<SoftGateKeeperDevice> soft_device;
 
     bool clear_state_if_needed_done;
+    bool is_running_gsi;
 };
 }// namespace android
 
diff --git a/init/Android.bp b/init/Android.bp
index 639d8d1..9aeb837 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -132,7 +132,7 @@
         "ueventd_parser.cpp",
         "util.cpp",
     ],
-    whole_static_libs: ["libcap"],
+    whole_static_libs: ["libcap", "com.android.sysprop.apex"],
     header_libs: ["bootimg_headers"],
     proto: {
         type: "lite",
diff --git a/init/Android.mk b/init/Android.mk
index 59d7f11..cc514ed 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -63,8 +63,9 @@
 LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
 
 # Set up the same mount points on the ramdisk that system-as-root contains.
-LOCAL_POST_INSTALL_CMD := \
-    mkdir -p $(TARGET_RAMDISK_OUT)/dev \
+LOCAL_POST_INSTALL_CMD := mkdir -p \
+    $(TARGET_RAMDISK_OUT)/apex \
+    $(TARGET_RAMDISK_OUT)/dev \
     $(TARGET_RAMDISK_OUT)/mnt \
     $(TARGET_RAMDISK_OUT)/proc \
     $(TARGET_RAMDISK_OUT)/sys \
@@ -93,6 +94,7 @@
     libselinux \
     libcap \
     libgsi \
+    libcom.android.sysprop.apex \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 538ed00..6511d29 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1119,13 +1119,21 @@
 }
 
 static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
-    if (SwitchToDefaultMountNamespace()) {
+    if (SetupRuntimeBionic()) {
         return Success();
     } else {
         return Error() << "Failed to setup runtime bionic";
     }
 }
 
+static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) {
+    if (SwitchToDefaultMountNamespace()) {
+        return Success();
+    } else {
+        return Error() << "Failed to enter into default mount namespace";
+    }
+}
+
 // Builtin-function-map start
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1177,6 +1185,7 @@
         {"start",                   {1,     1,    {false,  do_start}}},
         {"stop",                    {1,     1,    {false,  do_stop}}},
         {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
+        {"enter_default_mount_ns",  {0,     0,    {false,  do_enter_default_mount_ns}}},
         {"symlink",                 {2,     2,    {true,   do_symlink}}},
         {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
         {"trigger",                 {1,     1,    {false,  do_trigger}}},
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index e11d897..7cf4c3f 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -155,6 +155,10 @@
     // part of the product partition, e.g. because they are mounted read-write.
     CHECKCALL(mkdir("/mnt/product", 0755));
 
+    // /apex is used to mount APEXes
+    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"));
+
 #undef CHECKCALL
 
     // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 413fe8f..4161df2 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <ApexProperties.sysprop.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -132,9 +133,9 @@
     return true;
 }
 
-static bool IsBionicUpdatable() {
-    static bool result = android::base::GetBoolProperty("ro.apex.IsBionicUpdatable", false);
-    return result;
+static bool IsApexUpdatable() {
+    static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    return updatable;
 }
 
 static android::base::unique_fd bootstrap_ns_fd;
@@ -172,6 +173,11 @@
                          kBionicLibsMountPointDir64))
         return false;
 
+    // /apex is also a private mountpoint to give different sets of APEXes for
+    // the bootstrap and default mount namespaces. The processes running with
+    // the bootstrap namespace get APEXes from the read-only partition.
+    if (!(MakePrivate("/apex"))) return false;
+
     bootstrap_ns_fd.reset(OpenMountNamespace());
     bootstrap_ns_id = GetMountNamespaceId();
 
@@ -182,7 +188,7 @@
     // bind-mounted. In the namespace for post-apexd processes, the bionic from
     // the runtime APEX is bind-mounted.
     bool success = true;
-    if (IsBionicUpdatable() && !IsRecoveryMode()) {
+    if (IsApexUpdatable() && !IsRecoveryMode()) {
         // Creating a new namespace by cloning, saving, and switching back to
         // the original namespace.
         if (unshare(CLONE_NEWNS) == -1) {
@@ -227,8 +233,19 @@
         }
     }
 
+    LOG(INFO) << "Switched to default mount namespace";
+    return true;
+}
+
+// TODO(jiyong): remove this when /system/lib/libc.so becomes
+// a symlink to /apex/com.android.runtime/lib/bionic/libc.so
+bool SetupRuntimeBionic() {
+    if (IsRecoveryMode()) {
+        // We don't have multiple namespaces in recovery mode
+        return true;
+    }
     // Bind-mount bionic from the runtime APEX since it is now available. Note
-    // that in case of IsBionicUpdatable() == false, these mounts are over the
+    // that in case of IsApexUpdatable() == false, these mounts are over the
     // existing existing bind mounts for the bootstrap bionic, which effectively
     // becomes hidden.
     if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint,
@@ -238,7 +255,7 @@
                          kBionicLibsMountPointDir64))
         return false;
 
-    LOG(INFO) << "Switched to default mount namespace";
+    LOG(INFO) << "Runtime bionic is set up";
     return true;
 }
 
@@ -248,7 +265,7 @@
         return true;
     }
     if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
-        IsBionicUpdatable()) {
+        IsApexUpdatable()) {
         if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
             PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
             return false;
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index c41a449..4eef785 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -20,6 +20,7 @@
 namespace init {
 
 bool SetupMountNamespaces();
+bool SetupRuntimeBionic();
 bool SwitchToDefaultMountNamespace();
 bool SwitchToBootstrapMountNamespaceIfNeeded();
 
diff --git a/init/selinux.cpp b/init/selinux.cpp
index ee302c1..3fadfed 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -459,6 +459,8 @@
 
     selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
     selinux_android_restorecon("/dev/device-mapper", 0);
+
+    selinux_android_restorecon("/apex", 0);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 9d15af2..4518891 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -55,7 +55,7 @@
   }
 
   // Iterate through the maps and fill in the backtrace_map_t structure.
-  for (auto* map_info : *stack_maps_) {
+  for (const auto& map_info : *stack_maps_) {
     backtrace_map_t map;
     map.start = map_info->start;
     map.end = map_info->end;
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index fdfd705..a20be00 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -77,7 +77,7 @@
   //   f0000-f2000 0 r-- /system/lib/libc.so
   //   f2000-f3000 2000 rw- /system/lib/libc.so
   MapInfo* map_start = nullptr;
-  for (MapInfo* info : *maps) {
+  for (const auto& info : *maps) {
     if (map_start != nullptr) {
       if (map_start->name == info->name) {
         if (info->offset != 0 &&
@@ -96,7 +96,7 @@
     }
     if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
         !info->name.empty()) {
-      map_start = info;
+      map_start = info.get();
     }
   }
 }
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 1e4f72e..5da73e4 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -47,9 +47,9 @@
   size_t last = maps_.size();
   while (first < last) {
     size_t index = (first + last) / 2;
-    MapInfo* cur = maps_[index];
+    const auto& cur = maps_[index];
     if (pc >= cur->start && pc < cur->end) {
-      return cur;
+      return cur.get();
     } else if (pc < cur->start) {
       last = index;
     } else {
@@ -67,34 +67,31 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
+        maps_.emplace_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), 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(maps_.empty() ? nullptr : maps_.back(), start, end, offset, flags, name);
+  auto map_info =
+      std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
+                                flags, name);
   map_info->load_bias = load_bias;
-  maps_.push_back(map_info);
+  maps_.emplace_back(std::move(map_info));
 }
 
 void Maps::Sort() {
   std::sort(maps_.begin(), maps_.end(),
-            [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
+            [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+              return a->start < b->start; });
 
   // Set the prev_map values on the info objects.
   MapInfo* prev_map = nullptr;
-  for (MapInfo* map_info : maps_) {
+  for (const auto& map_info : maps_) {
     map_info->prev_map = prev_map;
-    prev_map = map_info;
-  }
-}
-
-Maps::~Maps() {
-  for (auto& map : maps_) {
-    delete map;
+    prev_map = map_info.get();
   }
 }
 
@@ -107,8 +104,9 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
+        maps_.emplace_back(
+            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+                        flags, name));
       });
 }
 
@@ -124,10 +122,6 @@
   // New maps will be added at the end without deleting the old ones.
   size_t last_map_idx = maps_.size();
   if (!Parse()) {
-    // Delete any maps added by the Parse call.
-    for (size_t i = last_map_idx; i < maps_.size(); i++) {
-      delete maps_[i];
-    }
     maps_.resize(last_map_idx);
     return false;
   }
@@ -135,17 +129,16 @@
   size_t total_entries = maps_.size();
   size_t search_map_idx = 0;
   for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
-    MapInfo* new_map_info = maps_[new_map_idx];
+    auto& new_map_info = maps_[new_map_idx];
     uint64_t start = new_map_info->start;
     uint64_t end = new_map_info->end;
     uint64_t flags = new_map_info->flags;
     std::string* name = &new_map_info->name;
     for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
-      MapInfo* info = maps_[old_map_idx];
+      auto& info = maps_[old_map_idx];
       if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
         // No need to check
         search_map_idx = old_map_idx + 1;
-        delete new_map_info;
         maps_[new_map_idx] = nullptr;
         total_entries--;
         break;
@@ -158,7 +151,7 @@
       // Never delete these maps, they may be in use. The assumption is
       // that there will only every be a handfull of these so waiting
       // to destroy them is not too expensive.
-      saved_maps_.push_back(info);
+      saved_maps_.emplace_back(std::move(info));
       maps_[old_map_idx] = nullptr;
       total_entries--;
     }
@@ -169,14 +162,14 @@
 
   // Now move out any of the maps that never were found.
   for (size_t i = search_map_idx; i < last_map_idx; i++) {
-    saved_maps_.push_back(maps_[i]);
+    saved_maps_.emplace_back(std::move(maps_[i]));
     maps_[i] = nullptr;
     total_entries--;
   }
 
   // Sort all of the values such that the nullptrs wind up at the end, then
   // resize them away.
-  std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+  std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
     if (a == nullptr) {
       return false;
     } else if (b == nullptr) {
@@ -189,10 +182,4 @@
   return true;
 }
 
-LocalUpdatableMaps::~LocalUpdatableMaps() {
-  for (auto map_info : saved_maps_) {
-    delete map_info;
-  }
-}
-
 }  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index 8caecc7..de9137a 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -92,9 +92,9 @@
 
   // Find the libc.so share library and use that for benchmark purposes.
   *build_id_map_info = nullptr;
-  for (unwindstack::MapInfo* map_info : maps) {
+  for (auto& map_info : maps) {
     if (map_info->offset == 0 && map_info->GetBuildID() != "") {
-      *build_id_map_info = map_info;
+      *build_id_map_info = map_info.get();
       break;
     }
   }
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 67fbed2..1784394 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -37,8 +38,16 @@
 
 class Maps {
  public:
+  virtual ~Maps() = default;
+
   Maps() = default;
-  virtual ~Maps();
+
+  // Maps are not copyable but movable, because they own pointers to MapInfo
+  // objects.
+  Maps(const Maps&) = delete;
+  Maps& operator=(const Maps&) = delete;
+  Maps(Maps&&) = default;
+  Maps& operator=(Maps&&) = default;
 
   MapInfo* Find(uint64_t pc);
 
@@ -51,11 +60,11 @@
 
   void Sort();
 
-  typedef std::vector<MapInfo*>::iterator iterator;
+  typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
 
-  typedef std::vector<MapInfo*>::const_iterator const_iterator;
+  typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
   const_iterator begin() const { return maps_.begin(); }
   const_iterator end() const { return maps_.end(); }
 
@@ -63,11 +72,11 @@
 
   MapInfo* Get(size_t index) {
     if (index >= maps_.size()) return nullptr;
-    return maps_[index];
+    return maps_[index].get();
   }
 
  protected:
-  std::vector<MapInfo*> maps_;
+  std::vector<std::unique_ptr<MapInfo>> maps_;
 };
 
 class RemoteMaps : public Maps {
@@ -90,14 +99,14 @@
 class LocalUpdatableMaps : public Maps {
  public:
   LocalUpdatableMaps() : Maps() {}
-  virtual ~LocalUpdatableMaps();
+  virtual ~LocalUpdatableMaps() = default;
 
   bool Reparse();
 
   const std::string GetMapsFile() const override;
 
  private:
-  std::vector<MapInfo*> saved_maps_;
+  std::vector<std::unique_ptr<MapInfo>> saved_maps_;
 };
 
 class BufferMaps : public Maps {
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index b4197f2..9e7a6ab 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -61,6 +61,26 @@
   ASSERT_EQ(0U, info->load_bias.load());
 }
 
+TEST(MapsTest, map_move) {
+  Maps maps;
+
+  maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+  maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+  maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+  Maps maps2 = std::move(maps);
+
+  ASSERT_EQ(3U, maps2.Total());
+  MapInfo* info = maps2.Get(0);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("fake_map", info->name);
+  ASSERT_EQ(0U, info->elf_offset);
+  ASSERT_EQ(0U, info->load_bias.load());
+}
+
 TEST(MapsTest, verify_parse_line) {
   MapInfo info(nullptr, 0, 0, 0, 0, "");
 
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index d88531f..2dc5118 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -49,7 +49,7 @@
     std::string str_name(name);
     maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
     if (elf != nullptr) {
-      MapInfo* map_info = *--maps_->end();
+      const auto& map_info = *--maps_->end();
       map_info->elf.reset(elf);
     }
   }
@@ -85,7 +85,7 @@
     AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
 
     AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
-    MapInfo* info = *--maps_->end();
+    const auto& info = *--maps_->end();
     info->load_bias = 0;
 
     elf = new ElfFake(new MemoryFake);
@@ -98,8 +98,8 @@
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
                elf);
-    info = *--maps_->end();
-    info->elf_offset = 0x8000;
+    const auto& info2 = *--maps_->end();
+    info2->elf_offset = 0x8000;
 
     process_memory_.reset(new MemoryFake);
   }
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 98b3aa1..18421e8 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -1329,7 +1329,7 @@
 static int last_killed_pid = -1;
 
 /* Kill one process specified by procp.  Returns the size of the process killed */
-static int kill_one_process(struct proc* procp) {
+static int kill_one_process(struct proc* procp, int min_oom_score) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
     char *taskname;
@@ -1340,6 +1340,9 @@
 #ifdef LMKD_LOG_STATS
     struct memory_stat mem_st = {};
     int memory_stat_parse_result = -1;
+#else
+    /* To prevent unused parameter warning */
+    (void)(min_oom_score);
 #endif
 
     taskname = proc_get_name(pid);
@@ -1385,10 +1388,12 @@
         if (memory_stat_parse_result == 0) {
             stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
                     procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
-                    mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns);
+                    mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
+                    min_oom_score);
         } else if (enable_stats_log) {
             stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
-                                          -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1);
+                                          -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
+                                          min_oom_score);
         }
 #endif
         result = tasksize;
@@ -1425,7 +1430,7 @@
             if (!procp)
                 break;
 
-            killed_size = kill_one_process(procp);
+            killed_size = kill_one_process(procp, min_score_adj);
             if (killed_size >= 0) {
 #ifdef LMKD_LOG_STATS
                 if (enable_stats_log && !lmk_state_change_start) {
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index 689e8ae..0c230ae 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -65,7 +65,8 @@
 stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
                               char const* process_name, int32_t oom_score, int64_t pgfault,
                               int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes, int64_t process_start_time_ns) {
+                              int64_t swap_in_bytes, int64_t process_start_time_ns,
+                              int32_t min_oom_score) {
     assert(ctx != NULL);
     int ret = -EINVAL;
     if (!ctx) {
@@ -117,5 +118,9 @@
         return ret;
     }
 
+    if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+        return ret;
+    }
+
     return write_to_logger(ctx, LOG_ID_STATS);
 }
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index f3abe11..2edba7a 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -88,7 +88,8 @@
 stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
                               char const* process_name, int32_t oom_score, int64_t pgfault,
                               int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
-                              int64_t swap_in_bytes, int64_t process_start_time_ns);
+                              int64_t swap_in_bytes, int64_t process_start_time_ns,
+                              int32_t min_oom_score);
 
 __END_DECLS
 
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
index 6b5d5d0..36a5f9f 100755
--- a/mkbootimg/unpack_bootimg.py
+++ b/mkbootimg/unpack_bootimg.py
@@ -103,10 +103,11 @@
                                  ) # header + kernel
     image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
 
-    second_offset = page_size * (
-        num_header_pages + num_kernel_pages + num_ramdisk_pages
-    )  # header + kernel + ramdisk
-    image_info_list.append((second_offset, second_size, 'second'))
+    if second_size > 0:
+        second_offset = page_size * (
+                num_header_pages + num_kernel_pages + num_ramdisk_pages
+                )  # header + kernel + ramdisk
+        image_info_list.append((second_offset, second_size, 'second'))
 
     if recovery_dtbo_size > 0:
         image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 28703d2..4b2dd07 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -68,6 +68,9 @@
 namespace.default.permitted.paths += /vendor/framework
 namespace.default.permitted.paths += /vendor/app
 namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /system/vendor/framework
+namespace.default.permitted.paths += /system/vendor/app
+namespace.default.permitted.paths += /system/vendor/priv-app
 namespace.default.permitted.paths += /odm/framework
 namespace.default.permitted.paths += /odm/app
 namespace.default.permitted.paths += /odm/priv-app
@@ -100,6 +103,9 @@
 namespace.default.asan.permitted.paths += /vendor/framework
 namespace.default.asan.permitted.paths += /vendor/app
 namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /system/vendor/framework
+namespace.default.asan.permitted.paths += /system/vendor/app
+namespace.default.asan.permitted.paths += /system/vendor/priv-app
 namespace.default.asan.permitted.paths += /odm/framework
 namespace.default.asan.permitted.paths += /odm/app
 namespace.default.asan.permitted.paths += /odm/priv-app
@@ -236,6 +242,7 @@
 
 namespace.sphal.permitted.paths  = /odm/${LIB}
 namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
 
 namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.sphal.asan.search.paths +=           /odm/${LIB}
@@ -278,6 +285,7 @@
 
 namespace.rs.permitted.paths  = /odm/${LIB}
 namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
 namespace.rs.permitted.paths += /data
 
 namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
@@ -323,6 +331,8 @@
 namespace.vndk.permitted.paths += /odm/${LIB}/egl
 namespace.vndk.permitted.paths += /vendor/${LIB}/hw
 namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
 # This is exceptionally required since android.hidl.memory@1.0-impl.so is here
 namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
@@ -390,6 +400,7 @@
 
 namespace.default.permitted.paths  = /odm
 namespace.default.permitted.paths += /vendor
+namespace.default.permitted.paths += /system/vendor
 #VNDK27#namespace.default.search.paths += /vendor/${LIB}/hw
 #VNDK27#namespace.default.search.paths += /vendor/${LIB}/egl
 
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index a5beb4e..54f4c98 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -184,6 +184,7 @@
 
 namespace.sphal.permitted.paths  = /odm/${LIB}
 namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
 
 namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.sphal.asan.search.paths +=           /odm/${LIB}
@@ -226,6 +227,7 @@
 
 namespace.rs.permitted.paths  = /odm/${LIB}
 namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
 namespace.rs.permitted.paths += /data
 
 namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
@@ -271,6 +273,7 @@
 namespace.vndk.permitted.paths += /odm/${LIB}/egl
 namespace.vndk.permitted.paths += /vendor/${LIB}/hw
 namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
 # This is exceptionally required since android.hidl.memory@1.0-impl.so is here
 namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7a826cc..f2e7a7c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -13,12 +13,6 @@
 
 # Cgroups are mounted right before early-init using list from /etc/cgroups.json
 on early-init
-    # Mount shared so changes propagate into child namespaces
-    # Do this before other processes are started from init. Otherwise,
-    # processes launched while the propagation type of / is 'private'
-    # won't get mount events from others.
-    mount rootfs rootfs / shared rec
-
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
@@ -43,6 +37,11 @@
 
     start ueventd
 
+    # Run apexd-bootstrap so that APEXes that provide critical libraries
+    # become available. Note that this is executed as exec_start to ensure that
+    # the libraries are available to the processes started after this statement.
+    exec_start apexd-bootstrap
+
 on init
     sysclktz 0
 
@@ -278,18 +277,9 @@
     write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}
     chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}
 
-    # Setup APEX mount point and its security context
-    mount tmpfs tmpfs /apex nodev noexec nosuid
-    chmod 0755 /apex
-    chown root root /apex
-    restorecon /apex
-
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
 
-    # Start apexd as soon as we can
-    start apexd
-
     # Start essential services.
     start servicemanager
     start hwservicemanager
@@ -426,8 +416,16 @@
     mkdir /data/bootchart 0755 shell shell
     bootchart start
 
-    # /data/apex is now available. Let apexd to scan and activate APEXes.
-    setprop apexd.data.status ready
+    # Make sure that apexd is started in the default namespace
+    enter_default_mount_ns
+
+    # /data/apex is now available. Start apexd to scan and activate APEXes.
+    mkdir /data/apex 0750 root system
+    mkdir /data/apex/active 0750 root system
+    mkdir /data/apex/backup 0700 root system
+    mkdir /data/apex/sessions 0700 root system
+    mkdir /data/pkg_staging 0750 system system
+    start apexd
 
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
@@ -544,12 +542,6 @@
 
     mkdir /data/anr 0775 system system
 
-    mkdir /data/apex 0750 root system
-    mkdir /data/apex/active 0750 root system
-    mkdir /data/apex/backup 0700 root system
-    mkdir /data/apex/sessions 0700 root system
-    mkdir /data/pkg_staging 0750 system system
-
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc
     mkdir /data/nfc/param 0770 nfc nfc
@@ -582,6 +574,12 @@
     mkdir /data/cache/backup_stage 0700 system system
     mkdir /data/cache/backup 0700 system system
 
+    # Wait for apexd to finish activating APEXes before starting more processes.
+    wait_for_prop apexd.status ready
+    # TODO(jiyong): remove setup_runtime_bionic
+    setup_runtime_bionic
+    parse_apex_configs
+
     init_user0
 
     # Set SELinux security contexts on upgrade or policy update.
@@ -590,14 +588,6 @@
     # load fsverity keys
     exec -- /system/bin/mini-keyctl -c /product/etc/security/cacerts_fsverity,/vendor/etc/security/cacerts_fsverity -k .fs-verity
 
-    # Wait for apexd to finish activating APEXes before starting more processes.
-    # This certainly reduces the parallelism but is required to make as many processes
-    # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
-    # so the impact on the booting time is not significant.
-    wait_for_prop apexd.status ready
-    setup_runtime_bionic
-    parse_apex_configs
-
     # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
 
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index e8c5d8e..f8e680d 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 9c7e807..0235370 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -22,6 +21,5 @@
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
     socket blastula_pool_secondary stream 660 root system
-    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 9908c99..3f3cc15 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 0b5edff..fae38c9 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,7 +5,6 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket blastula_pool stream 660 root system
-    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -22,6 +21,5 @@
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
     socket blastula_pool_secondary stream 660 root system
-    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index e1de130..2b35819 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -27,6 +27,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -99,14 +100,21 @@
 
 static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
                            uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
-                           mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
+                           mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
+                           bool use_esdfs) {
+    // Add new options at the end of the vector.
+    std::vector<std::string> new_opts_list;
+    if (multi_user) new_opts_list.push_back("multiuser,");
+    if (derive_gid) new_opts_list.push_back("derive_gid,");
+    if (default_normal) new_opts_list.push_back("default_normal,");
+    if (unshared_obb) new_opts_list.push_back("unshared_obb,");
     // Try several attempts, each time with one less option, to gracefully
     // handle older kernels that aren't updated yet.
-    for (int i = 0; i < 4; i++) {
+    for (int i = 0; i <= new_opts_list.size(); ++i) {
         std::string new_opts;
-        if (multi_user && i < 3) new_opts += "multiuser,";
-        if (derive_gid && i < 2) new_opts += "derive_gid,";
-        if (default_normal && i < 1) new_opts += "default_normal,";
+        for (int j = 0; j < new_opts_list.size() - i; ++j) {
+            new_opts += new_opts_list[j];
+        }
 
         auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
                                                 fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
@@ -142,13 +150,14 @@
     return true;
 }
 
-static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
-                                     const std::string& dest_path, uid_t fsuid, gid_t fsgid,
-                                     bool multi_user, userid_t userid, gid_t gid, mode_t mask,
-                                     bool derive_gid, bool default_normal, bool use_esdfs) {
+static bool sdcardfs_setup_secondary(const std::string& default_path,
+                                     const std::string& source_path, const std::string& dest_path,
+                                     uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid,
+                                     gid_t gid, mode_t mask, bool derive_gid, bool default_normal,
+                                     bool unshared_obb, bool use_esdfs) {
     if (use_esdfs) {
         return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
-                              derive_gid, default_normal, use_esdfs);
+                              derive_gid, default_normal, unshared_obb, use_esdfs);
     } else {
         return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
     }
@@ -156,7 +165,7 @@
 
 static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
                          gid_t gid, userid_t userid, bool multi_user, bool full_write,
-                         bool derive_gid, bool default_normal, bool use_esdfs) {
+                         bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -167,16 +176,17 @@
         // Multi-user storage is fully isolated per user, so "other"
         // permissions are completely masked off.
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+                            use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
-                                      default_normal, use_esdfs) ||
+                                      default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
-                                      derive_gid, default_normal, use_esdfs) ||
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
-                                      default_normal, use_esdfs)) {
+                                      default_normal, unshared_obb, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -184,16 +194,17 @@
         // the Android directories are masked off to a single user
         // deep inside attr_from_stat().
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+                            use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
-                                      derive_gid, default_normal, use_esdfs) ||
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
-                                      derive_gid, default_normal, use_esdfs) ||
+                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
-                                      default_normal, use_esdfs)) {
+                                      default_normal, unshared_obb, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
@@ -216,7 +227,8 @@
                << "    -U: specify user ID that owns device"
                << "    -m: source_path is multi-user"
                << "    -w: runtime write mount has full write access"
-               << "    -P  preserve owners on the lower file system";
+               << "    -P: preserve owners on the lower file system"
+               << "    -o: obb dir doesn't need to be shared between users";
     return 1;
 }
 
@@ -230,6 +242,7 @@
     bool full_write = false;
     bool derive_gid = false;
     bool default_normal = false;
+    bool unshared_obb = false;
     int i;
     struct rlimit rlim;
     int fs_version;
@@ -238,7 +251,7 @@
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
     int opt;
-    while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
+    while ((opt = getopt(argc, argv, "u:g:U:mwGio")) != -1) {
         switch (opt) {
             case 'u':
                 uid = strtoul(optarg, NULL, 10);
@@ -261,8 +274,12 @@
             case 'i':
                 default_normal = true;
                 break;
+            case 'o':
+                unshared_obb = true;
+                break;
             case '?':
             default:
+                LOG(ERROR) << "Unknown option: '" << opt << "'";
                 return usage();
         }
     }
@@ -304,6 +321,6 @@
     }
 
     run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
-                 default_normal, !should_use_sdcardfs());
+                 default_normal, unshared_obb, !should_use_sdcardfs());
     return 1;
 }
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f08cf93..7ad6f1c 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -59,6 +59,7 @@
     name: "r",
     defaults: ["toolbox_defaults"],
     srcs: ["r.c"],
+    vendor_available: true,
 }
 
 // We build BSD grep separately (but see http://b/111849261).
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 41263e5..9a71ae3 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -17,8 +17,8 @@
 #include <getopt.h>
 #include <stdbool.h>
 #include <stdint.h>
-#include <string.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
@@ -34,28 +34,24 @@
 #define REQ_BUFFER_SIZE 4096
 static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
 
-static const char *ss_data_root;
-static const char *trusty_devname;
-static const char *rpmb_devname;
-static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+static const char* ss_data_root;
+static const char* trusty_devname;
+static const char* rpmb_devname;
+static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
 
-static const char *_sopts = "hp:d:r:";
-static const struct option _lopts[] =  {
-    {"help",       no_argument,       NULL, 'h'},
-    {"trusty_dev", required_argument, NULL, 'd'},
-    {"data_path",  required_argument, NULL, 'p'},
-    {"rpmb_dev",   required_argument, NULL, 'r'},
-    {0, 0, 0, 0}
-};
+static const char* _sopts = "hp:d:r:";
+static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
+                                       {"trusty_dev", required_argument, NULL, 'd'},
+                                       {"data_path", required_argument, NULL, 'p'},
+                                       {"rpmb_dev", required_argument, NULL, 'r'},
+                                       {0, 0, 0, 0}};
 
-static void show_usage_and_exit(int code)
-{
+static void show_usage_and_exit(int code) {
     ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
     exit(code);
 }
 
-static int drop_privs(void)
-{
+static int drop_privs(void) {
     struct __user_cap_header_struct capheader;
     struct __user_cap_data_struct capdata[2];
 
@@ -95,12 +91,10 @@
     return 0;
 }
 
-static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
-{
+static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
     int rc;
 
-    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
-        (msg->cmd != STORAGE_RPMB_SEND)) {
+    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
         /*
          * handling post commit messages on non rpmb commands are not
          * implemented as there is no use case for this yet.
@@ -119,42 +113,42 @@
     }
 
     switch (msg->cmd) {
-    case STORAGE_FILE_DELETE:
-        rc = storage_file_delete(msg, req, req_len);
-        break;
+        case STORAGE_FILE_DELETE:
+            rc = storage_file_delete(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_OPEN:
-        rc = storage_file_open(msg, req, req_len);
-        break;
+        case STORAGE_FILE_OPEN:
+            rc = storage_file_open(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_CLOSE:
-        rc = storage_file_close(msg, req, req_len);
-        break;
+        case STORAGE_FILE_CLOSE:
+            rc = storage_file_close(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_WRITE:
-        rc = storage_file_write(msg, req, req_len);
-        break;
+        case STORAGE_FILE_WRITE:
+            rc = storage_file_write(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_READ:
-        rc = storage_file_read(msg, req, req_len);
-        break;
+        case STORAGE_FILE_READ:
+            rc = storage_file_read(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_GET_SIZE:
-        rc = storage_file_get_size(msg, req, req_len);
-        break;
+        case STORAGE_FILE_GET_SIZE:
+            rc = storage_file_get_size(msg, req, req_len);
+            break;
 
-    case STORAGE_FILE_SET_SIZE:
-        rc = storage_file_set_size(msg, req, req_len);
-        break;
+        case STORAGE_FILE_SET_SIZE:
+            rc = storage_file_set_size(msg, req, req_len);
+            break;
 
-    case STORAGE_RPMB_SEND:
-        rc = rpmb_send(msg, req, req_len);
-        break;
+        case STORAGE_RPMB_SEND:
+            rc = rpmb_send(msg, req, req_len);
+            break;
 
-    default:
-        ALOGE("unhandled command 0x%x\n", msg->cmd);
-        msg->result = STORAGE_ERR_UNIMPLEMENTED;
-        rc = 1;
+        default:
+            ALOGE("unhandled command 0x%x\n", msg->cmd);
+            msg->result = STORAGE_ERR_UNIMPLEMENTED;
+            rc = 1;
     }
 
     if (rc > 0) {
@@ -164,58 +158,50 @@
     return rc;
 }
 
-static int proxy_loop(void)
-{
+static int proxy_loop(void) {
     ssize_t rc;
     struct storage_msg msg;
 
     /* enter main message handling loop */
     while (true) {
-
         /* get incoming message */
         rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
-        if (rc < 0)
-            return rc;
+        if (rc < 0) return rc;
 
         /* handle request */
         req_buffer[rc] = 0; /* force zero termination */
         rc = handle_req(&msg, req_buffer, rc);
-        if (rc)
-            return rc;
+        if (rc) return rc;
     }
 
     return 0;
 }
 
-static void parse_args(int argc, char *argv[])
-{
+static void parse_args(int argc, char* argv[]) {
     int opt;
     int oidx = 0;
 
     while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
         switch (opt) {
+            case 'd':
+                trusty_devname = strdup(optarg);
+                break;
 
-        case 'd':
-            trusty_devname = strdup(optarg);
-            break;
+            case 'p':
+                ss_data_root = strdup(optarg);
+                break;
 
-        case 'p':
-            ss_data_root = strdup(optarg);
-            break;
+            case 'r':
+                rpmb_devname = strdup(optarg);
+                break;
 
-        case 'r':
-            rpmb_devname = strdup(optarg);
-            break;
-
-        default:
-            ALOGE("unrecognized option (%c):\n", opt);
-            show_usage_and_exit(EXIT_FAILURE);
+            default:
+                ALOGE("unrecognized option (%c):\n", opt);
+                show_usage_and_exit(EXIT_FAILURE);
         }
     }
 
-    if (ss_data_root == NULL ||
-        trusty_devname == NULL ||
-        rpmb_devname == NULL) {
+    if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {
         ALOGE("missing required argument(s)\n");
         show_usage_and_exit(EXIT_FAILURE);
     }
@@ -226,31 +212,26 @@
     ALOGI("rpmb dev: %s\n", rpmb_devname);
 }
 
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
     int rc;
 
     /* drop privileges */
-    if (drop_privs() < 0)
-        return EXIT_FAILURE;
+    if (drop_privs() < 0) return EXIT_FAILURE;
 
     /* parse arguments */
     parse_args(argc, argv);
 
     /* initialize secure storage directory */
     rc = storage_init(ss_data_root);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    if (rc < 0) return EXIT_FAILURE;
 
     /* open rpmb device */
     rc = rpmb_open(rpmb_devname);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    if (rc < 0) return EXIT_FAILURE;
 
     /* connect to Trusty secure storage server */
     rc = ipc_connect(trusty_devname, ss_srv_name);
-    if (rc < 0)
-        return EXIT_FAILURE;
+    if (rc < 0) return EXIT_FAILURE;
 
     /* enter main loop */
     rc = proxy_loop();
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 9c79105..e706d0a 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -54,14 +54,12 @@
 
 #ifdef RPMB_DEBUG
 
-static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
-{
+static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
     size_t i;
 
     printf("%s @%p [%zu]", prefix, buf, size);
     for (i = 0; i < size; i++) {
-        if (i && i % 32 == 0)
-            printf("\n%*s", (int) strlen(prefix), "");
+        if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
         printf(" %02x", buf[i]);
     }
     printf("\n");
@@ -70,34 +68,29 @@
 
 #endif
 
-
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
-{
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
     int rc;
     struct {
         struct mmc_ioc_multi_cmd multi;
         struct mmc_ioc_cmd cmd_buf[3];
     } mmc = {};
-    struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
-    const struct storage_rpmb_send_req *req = r;
+    struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
+    const struct storage_rpmb_send_req* req = r;
 
     if (req_len < sizeof(*req)) {
-        ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
-              req_len, sizeof(*req));
+        ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
         msg->result = STORAGE_ERR_NOT_VALID;
         goto err_response;
     }
 
-    size_t expected_len =
-            sizeof(*req) + req->reliable_write_size + req->write_size;
+    size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
     if (req_len != expected_len) {
-        ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
-              req_len, expected_len);
+        ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
         msg->result = STORAGE_ERR_NOT_VALID;
         goto err_response;
     }
 
-    const uint8_t *write_buf = req->payload;
+    const uint8_t* write_buf = req->payload;
     if (req->reliable_write_size) {
         if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
             ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
@@ -143,8 +136,7 @@
     }
 
     if (req->read_size) {
-        if (req->read_size % MMC_BLOCK_SIZE != 0 ||
-            req->read_size > sizeof(read_buf)) {
+        if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
             ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
             msg->result = STORAGE_ERR_NOT_VALID;
             goto err_response;
@@ -152,8 +144,7 @@
 
         cmd->write_flag = MMC_WRITE_FLAG_R;
         cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
-        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
-        cmd->blksz = MMC_BLOCK_SIZE;
+        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;
         cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
         mmc_ioc_cmd_set_data((*cmd), read_buf);
 #ifdef RPMB_DEBUG
@@ -170,8 +161,7 @@
         goto err_response;
     }
 #ifdef RPMB_DEBUG
-    if (req->read_size)
-        print_buf("response: ", read_buf, req->read_size);
+    if (req->read_size) print_buf("response: ", read_buf, req->read_size);
 #endif
 
     if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
@@ -188,24 +178,19 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int rpmb_open(const char *rpmb_devname)
-{
+int rpmb_open(const char* rpmb_devname) {
     int rc;
 
     rc = open(rpmb_devname, O_RDWR, 0);
     if (rc < 0) {
-        ALOGE("unable (%d) to open rpmb device '%s': %s\n",
-              errno, rpmb_devname, strerror(errno));
+        ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
         return rc;
     }
     rpmb_fd = rc;
     return 0;
 }
 
-void rpmb_close(void)
-{
+void rpmb_close(void) {
     close(rpmb_fd);
     rpmb_fd = -1;
 }
-
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 85cff44..5107361 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,6 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
-int rpmb_open(const char *rpmb_devname);
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+int rpmb_open(const char* rpmb_devname);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
 void rpmb_close(void);