Merge "Special case init for android_is_in_vendor_process."
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 3286959..3d5d9db 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -222,7 +222,9 @@
         "     all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
         " $ADB_VENDOR_KEYS         colon-separated list of keys (files or directories)\n"
         " $ANDROID_SERIAL          serial number to connect to (see -s)\n"
-        " $ANDROID_LOG_TAGS        tags to be used by logcat (see logcat --help)\n");
+        " $ANDROID_LOG_TAGS        tags to be used by logcat (see logcat --help)\n"
+        " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+    );
     // clang-format on
 }
 
@@ -1003,7 +1005,8 @@
 #endif /* !defined(_WIN32) */
 }
 
-static bool wait_for_device(const char* service) {
+static bool wait_for_device(const char* service,
+                            std::optional<std::chrono::milliseconds> timeout = std::nullopt) {
     std::vector<std::string> components = android::base::Split(service, "-");
     if (components.size() < 3 || components.size() > 4) {
         fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
@@ -1041,6 +1044,13 @@
     }
 
     std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
+    if (timeout) {
+        std::thread([timeout]() {
+            std::this_thread::sleep_for(*timeout);
+            fprintf(stderr, "timeout expired while waiting for device\n");
+            _exit(1);
+        }).detach();
+    }
     return adb_command(cmd);
 }
 
@@ -1082,8 +1092,21 @@
     }
 
     // Wait for the device to go away.
+    TransportType previous_type;
+    const char* previous_serial;
+    TransportId previous_id;
+    adb_get_transport(&previous_type, &previous_serial, &previous_id);
+
     adb_set_transport(kTransportAny, nullptr, transport_id);
     wait_for_device("wait-for-disconnect");
+
+    // Wait for the device to come back.
+    // If we were using a specific transport ID, there's nothing we can wait for.
+    if (previous_id == 0) {
+        adb_set_transport(previous_type, previous_serial, 0);
+        wait_for_device("wait-for-device", 3000ms);
+    }
+
     return true;
 }
 
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 7999ddc..ce494ee 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -27,68 +27,62 @@
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 
-void remount_service(unique_fd fd, const std::string& cmd) {
-    static constexpr char remount_cmd[] = "/system/bin/remount";
-    static constexpr char remount_failed[] = "remount failed\n";
+static constexpr char kRemountCmd[] = "/system/bin/remount";
 
+static bool do_remount(int fd, const std::string& cmd) {
     if (getuid() != 0) {
-        WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
-        WriteFdExactly(fd.get(), remount_failed);
-        return;
+        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
+        return false;
     }
 
-    auto pid = vfork();
+    auto pid = fork();
     if (pid < 0) {
-        WriteFdFmt(fd.get(), "Failed to fork to %s: %s\n", remount_cmd, strerror(errno));
-        WriteFdExactly(fd.get(), remount_failed);
-        return;
+        WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
+        return false;
     }
 
     if (pid == 0) {
         // child side of the fork
-        fcntl(fd.get(), F_SETFD, 0);
-        dup2(fd.get(), STDIN_FILENO);
-        dup2(fd.get(), STDOUT_FILENO);
-        dup2(fd.get(), STDERR_FILENO);
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        dup2(fd, STDERR_FILENO);
 
-        execl(remount_cmd, remount_cmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
-        _exit(-errno ?: 42);
+        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.get(), "Failed to wait for %s: %s\n", remount_cmd, strerror(errno));
-        goto err;
-    }
-
-    if (ret != pid) {
-        WriteFdFmt(fd.get(), "pid %d and waitpid return %d do not match for %s\n",
-                   static_cast<int>(pid), static_cast<int>(ret), remount_cmd);
-        goto err;
+        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;
     }
 
     if (WIFSIGNALED(wstatus)) {
-        WriteFdFmt(fd.get(), "%s terminated with signal %s\n", remount_cmd,
+        WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
                    strsignal(WTERMSIG(wstatus)));
-        goto err;
+        return false;
     }
 
     if (!WIFEXITED(wstatus)) {
-        WriteFdFmt(fd.get(), "%s stopped with status 0x%x\n", remount_cmd, wstatus);
-        goto err;
+        WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
+        return false;
     }
 
     if (WEXITSTATUS(wstatus)) {
-        WriteFdFmt(fd.get(), "%s exited with status %d\n", remount_cmd,
-                   static_cast<signed char>(WEXITSTATUS(wstatus)));
-        goto err;
+        WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
+        return false;
     }
 
-    WriteFdExactly(fd.get(), "remount succeeded\n");
-    return;
+    return true;
+}
 
-err:
-    WriteFdExactly(fd.get(), remount_failed);
+void remount_service(unique_fd fd, const std::string& cmd) {
+    const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
+    WriteFdFmt(fd.get(), "remount %s\n", success);
 }
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 658261e..889229f 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -52,14 +52,13 @@
 }
 
 static bool make_block_device_writable(const std::string& dev) {
-    int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
+    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);
-    unix_close(fd);
     return result;
 }
 
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 598f2cd..f4458a2 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -270,29 +270,15 @@
             bool started = false;
             bool running = true;
             while (running) {
-                int timeout = -1;
-                if (!bound || !started) {
-                    timeout = 5000 /*ms*/;
-                }
-
                 adb_pollfd pfd[2] = {
                   { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
                   { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
                 };
-                int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout));
+                int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, -1));
                 if (rc == -1) {
                     PLOG(FATAL) << "poll on USB control fd failed";
                 } else if (rc == 0) {
-                    // Something in the kernel presumably went wrong.
-                    // Close our endpoints, wait for a bit, and then try again.
-                    StopWorker();
-                    aio_context_.reset();
-                    read_fd_.reset();
-                    write_fd_.reset();
-                    control_fd_.reset();
-                    std::this_thread::sleep_for(5s);
-                    HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
-                    return;
+                    LOG(FATAL) << "poll on USB control fd returned 0";
                 }
 
                 if (pfd[1].revents) {
@@ -654,9 +640,10 @@
 }
 
 void usb_init() {
-    if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
-        usb_init_legacy();
-    } else {
+    bool use_nonblocking = android::base::GetBoolProperty("persist.adb.nonblocking_ffs", true);
+    if (use_nonblocking) {
         std::thread(usb_ffs_open_thread).detach();
+    } else {
+        usb_init_legacy();
     }
 }
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9a74fb3..b9f738d 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -53,12 +53,27 @@
 
 // Android Wear has been using port 5601 in all of its documentation/tooling,
 // but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
-// Avoid stomping on their port by limiting the number of emulators that can be
-// connected.
-#define ADB_LOCAL_TRANSPORT_MAX 16
+// Avoid stomping on their port by restricting the active scanning range.
+// Once emulators self-(re-)register, they'll have to avoid 5601 in their own way.
+static int adb_local_transport_max_port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT + 16 * 2 - 1;
 
 static std::mutex& local_transports_lock = *new std::mutex();
 
+static void adb_local_transport_max_port_env_override() {
+    const char* env_max_s = getenv("ADB_LOCAL_TRANSPORT_MAX_PORT");
+    if (env_max_s != nullptr) {
+        size_t env_max;
+        if (ParseUint(&env_max, env_max_s, nullptr) && env_max < 65536) {
+            // < DEFAULT_ADB_LOCAL_TRANSPORT_PORT harmlessly mimics ADB_EMU=0
+            adb_local_transport_max_port = env_max;
+            D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT read as %d", adb_local_transport_max_port);
+        } else {
+            D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT '%s' invalid or >= 65536, so ignored",
+              env_max_s);
+        }
+    }
+}
+
 // We keep a map from emulator port to transport.
 // TODO: weak_ptr?
 static auto& local_transports GUARDED_BY(local_transports_lock) =
@@ -110,7 +125,6 @@
             D("reconnect failed: %s", response.c_str());
             return ReconnectResult::Retry;
         }
-
         // This invokes the part of register_socket_transport() that needs to be
         // invoked if the atransport* has already been setup. This eventually
         // calls atransport->SetConnection() with a newly created Connection*
@@ -168,12 +182,10 @@
 #if ADB_HOST
 
 static void PollAllLocalPortsForEmulator() {
-    int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    int count = ADB_LOCAL_TRANSPORT_MAX;
-
     // Try to connect to any number of running emulator instances.
-    for ( ; count > 0; count--, port += 2 ) {
-        local_connect(port);
+    for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
+         port += 2) {
+        local_connect(port);  // Note, uses port and port-1, so '=max_port' is OK.
     }
 }
 
@@ -289,6 +301,7 @@
 #if ADB_HOST
     D("transport: local client init");
     std::thread(client_socket_thread, port).detach();
+    adb_local_transport_max_port_env_override();
 #elif !defined(__ANDROID__)
     // Host adbd.
     D("transport: local server init");
@@ -371,10 +384,6 @@
         if (existing_transport != nullptr) {
             D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
             fail = -1;
-        } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
-            // Too many emulators.
-            D("cannot register more emulators. Maximum is %d", ADB_LOCAL_TRANSPORT_MAX);
-            fail = -1;
         } else {
             local_transports[adb_port] = t;
         }
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index faa845d..7c65dc3 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -41,7 +41,14 @@
   HANDLE handle =
       CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
                         (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
-  if (handle == nullptr) return nullptr;
+  if (handle == nullptr) {
+    // http://b/119818070 "app crashes when reading asset of zero length".
+    // Return a MappedFile that's only valid for reading the size.
+    if (length == 0) {
+      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+    }
+    return nullptr;
+  }
   void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
                              file_offset, file_length);
   if (base == nullptr) {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index ed955ea..ea49798 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -293,6 +293,8 @@
     {"kernel_panic,dsps", 166},
     {"kernel_panic,wcnss", 167},
     {"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
+    {"recovery,quiescent", 169},
+    {"reboot,quiescent", 170},
 };
 
 // Converts a string value representing the reason the system booted to an
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_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 40da36d..e61f588 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -769,9 +769,10 @@
 
     // in recovery, fastbootd, or gsi mode, not allowed!
     if (fs_mgr_access("/system/bin/recovery")) return true;
-    if (android::gsi::IsGsiRunning()) return true;
-
-    return false;
+    auto save_errno = errno;
+    auto ret = android::gsi::IsGsiRunning();
+    errno = save_errno;
+    return ret;
 }
 
 }  // namespace
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..5b77280 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,45 @@
     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 += s.st_blksize;
+        } else {
+            extents->push_back(
+                    fiemap_extent{.fe_logical = block_number,
+                                  .fe_physical = static_cast<uint64_t>(block) * s.st_blksize,
+                                  .fe_length = static_cast<uint64_t>(s.st_blksize),
+                                  .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 +582,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,28 +599,35 @@
 
     // 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;
     fmap->bdev_path_ = bdev_path;
-    fmap->file_fd_ = std::move(file_fd);
     fmap->file_size_ = file_size;
     fmap->bdev_size_ = bdevsz;
     fmap->fs_type_ = fs_type;
     fmap->block_size_ = blocksz;
 
-    LOG(INFO) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
-              << bdev_path;
+    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..66eb9ae 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -33,8 +33,13 @@
 #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"
+
+namespace android {
+namespace fiemap_writer {
 
 using namespace std;
 using namespace std::string_literals;
@@ -59,6 +64,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 +94,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 +109,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 +119,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 +191,151 @@
     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);
+}
+
+static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
+    std::string result;
+    for (int i = 0; i < num_files; i++) {
+        std::string path = base_path + android::base::StringPrintf(".%04d", i);
+        std::string data;
+        if (!android::base::ReadFileToString(path, &data)) {
+            return {};
+        }
+        result += data;
+    }
+    return result;
+}
+
+TEST_F(SplitFiemapTest, WriteWholeFile) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks1) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+
+    // Write in chunks of 1000 (so some writes straddle the boundary of two
+    // files).
+    size_t bytes_written = 0;
+    while (bytes_written < kSize) {
+        size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
+        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+        ASSERT_TRUE(ptr->Write(data, to_write));
+        bytes_written += to_write;
+    }
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks2) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+
+    // Write in chunks of 32KiB so every write is exactly at the end of the
+    // current file.
+    size_t bytes_written = 0;
+    while (bytes_written < kSize) {
+        size_t to_write = std::min(kSize - bytes_written, kChunkSize);
+        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+        ASSERT_TRUE(ptr->Write(data, to_write));
+        bytes_written += to_write;
+    }
+
+    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+    auto actual = ReadSplitFiles(testfile, 3);
+    ASSERT_EQ(expected.size(), actual.size());
+    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WritePastEnd) {
+    static constexpr size_t kChunkSize = 32768;
+    static constexpr size_t kSize = kChunkSize * 3;
+    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+    ASSERT_NE(ptr, nullptr);
+
+    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+    for (size_t i = 0; i < kSize / sizeof(int); i++) {
+        buffer[i] = i;
+    }
+    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+    ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
+}
+
 class VerifyBlockWritesExt4 : public ::testing::Test {
     // 2GB Filesystem and 4k block size by default
     static constexpr uint64_t block_size = 4096;
@@ -258,6 +435,11 @@
     return true;
 }
 
+}  // namespace fiemap_writer
+}  // namespace android
+
+using namespace android::fiemap_writer;
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     if (argc <= 1) {
@@ -273,7 +455,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..9486122 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;
@@ -92,9 +88,6 @@
     // Block device on which we have created the file.
     std::string bdev_path_;
 
-    // File descriptors for the file and block device
-    ::android::base::unique_fd file_fd_;
-
     // Size in bytes of the file this class is writing
     uint64_t file_size_;
 
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..07f3c10
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
@@ -0,0 +1,96 @@
+/*
+ * 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 <android-base/unique_fd.h>
+
+#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);
+
+    // Return whether all components of a split file still have pinned extents.
+    bool HasPinnedExtents() const;
+
+    // Helper method for writing data that spans files. Note there is no seek
+    // method (yet); this starts at 0 and increments the position by |bytes|.
+    bool Write(const void* data, uint64_t bytes);
+
+    // Flush all writes to all split files.
+    bool Flush();
+
+    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;
+
+    // Most recently open file and position for Write().
+    size_t cursor_index_ = 0;
+    uint64_t cursor_file_pos_ = 0;
+    android::base::unique_fd cursor_fd_;
+};
+
+}  // 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..dbb67a8
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -0,0 +1,293 @@
+/*
+ * 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;
+}
+
+bool SplitFiemap::HasPinnedExtents() const {
+    for (const auto& file : files_) {
+        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+            return false;
+        }
+    }
+    return true;
+}
+
+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_;
+}
+
+bool SplitFiemap::Write(const void* data, uint64_t bytes) {
+    // Open the current file.
+    FiemapWriter* file = files_[cursor_index_].get();
+
+    const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
+    uint64_t bytes_remaining = bytes;
+    while (bytes_remaining) {
+        // How many bytes can we write into the current file?
+        uint64_t file_bytes_left = file->size() - cursor_file_pos_;
+        if (!file_bytes_left) {
+            if (cursor_index_ == files_.size() - 1) {
+                LOG(ERROR) << "write past end of file requested";
+                return false;
+            }
+
+            // No space left in the current file, but we have more files to
+            // use, so prep the next one.
+            cursor_fd_ = {};
+            cursor_file_pos_ = 0;
+            file = files_[++cursor_index_].get();
+            file_bytes_left = file->size();
+        }
+
+        // Open the current file if it's not open.
+        if (cursor_fd_ < 0) {
+            cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
+            if (cursor_fd_ < 0) {
+                PLOG(ERROR) << "open failed: " << file->file_path();
+                return false;
+            }
+            CHECK(cursor_file_pos_ == 0);
+        }
+
+        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+            LOG(ERROR) << "file is no longer pinned: " << file->file_path();
+            return false;
+        }
+
+        uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
+        if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
+            PLOG(ERROR) << "write failed: " << file->file_path();
+            return false;
+        }
+        data_ptr += bytes_to_write;
+        bytes_remaining -= bytes_to_write;
+        cursor_file_pos_ += bytes_to_write;
+    }
+
+    // If we've reached the end of the current file, close it for sanity.
+    if (cursor_file_pos_ == file->size()) {
+        cursor_fd_ = {};
+    }
+    return true;
+}
+
+bool SplitFiemap::Flush() {
+    for (const auto& file : files_) {
+        unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "open failed: " << file->file_path();
+            return false;
+        }
+        if (fsync(fd)) {
+            PLOG(ERROR) << "fsync failed: " << file->file_path();
+            return false;
+        }
+    }
+    return true;
+}
+
+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/init/service.cpp b/init/service.cpp
index 2186a85..cba42c4 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1059,7 +1059,7 @@
         std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
         if (!WriteStringToFile(oom_str, oom_file)) {
-            PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+            PLOG(ERROR) << "couldn't write oom_score_adj";
         }
     }
 
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 0fd2a3a..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,7 +27,6 @@
     name: "libasyncio",
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
-    double_loadable: true,
     recovery_available: true,
     host_supported: true,
     srcs: [
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/libkeyutils/Android.bp b/libkeyutils/Android.bp
index e816926..dda491a 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -19,13 +19,14 @@
 
 cc_binary {
     name: "mini-keyctl",
-    srcs: ["mini_keyctl.cpp"],
-
+    srcs: [
+        "mini_keyctl.cpp",
+        "mini_keyctl_utils.cpp"
+    ],
     shared_libs: [
         "libbase",
         "libkeyutils",
         "liblog",
     ],
-
-    cflags: ["-Werror", "-Wall", "-Wextra"],
+    cflags: ["-Werror", "-Wall", "-Wextra", "-fexceptions"],
 }
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
index abc8f82..4fe4c3c 100644
--- a/libkeyutils/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl.cpp
@@ -18,159 +18,57 @@
  * A tool loads keys to keyring.
  */
 
-#include <dirent.h>
-#include <errno.h>
-#include <sys/types.h>
+#include "mini_keyctl_utils.h"
+
 #include <unistd.h>
 
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <keyutils.h>
-
-static constexpr int kMaxCertSize = 4096;
-
-// Add all the certs from directory path to keyring with keyring_id. Returns the number of keys
-// added.
-int AddKeys(const std::string& path, const key_serial_t keyring_id, const std::string& keyring_desc,
-            int start_index) {
-  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(path.c_str()), closedir);
-  if (!dir) {
-    PLOG(WARNING) << "Failed to open directory " << path;
-    return 0;
-  }
-  int keys_added = 0;
-  struct dirent* dp;
-  while ((dp = readdir(dir.get())) != NULL) {
-    if (dp->d_type != DT_REG) {
-      continue;
-    }
-    std::string cert_path = path + "/" + dp->d_name;
-    std::string cert_buf;
-    if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
-      LOG(ERROR) << "Failed to read " << cert_path;
-      continue;
-    }
-
-    if (cert_buf.size() > kMaxCertSize) {
-      LOG(ERROR) << "Certficate size too large: " << cert_path;
-      continue;
-    }
-
-    // Add key to keyring.
-    int key_desc_index = keys_added + start_index;
-    std::string key_desc = keyring_desc + "-key" + std::to_string(key_desc_index);
-    key_serial_t key =
-        add_key("asymmetric", key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
-    if (key < 0) {
-      PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
-      continue;
-    }
-    keys_added++;
-  }
-  return keys_added;
-}
-
-std::vector<std::string> SplitBySpace(const std::string& s) {
-  std::istringstream iss(s);
-  return std::vector<std::string>{std::istream_iterator<std::string>{iss},
-                                  std::istream_iterator<std::string>{}};
-}
-
-// Find the keyring id. Because request_key(2) syscall is not available or the key is
-// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
-// information in the descritption section depending on the key type, only the first word in the
-// keyring description is used for searching.
-bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
-  if (!keyring_id) {
-    LOG(ERROR) << "keyring_id is null";
-    return false;
-  }
-
-  // Only keys allowed by SELinux rules will be shown here.
-  std::ifstream proc_keys_file("/proc/keys");
-  if (!proc_keys_file.is_open()) {
-    PLOG(ERROR) << "Failed to open /proc/keys";
-    return false;
-  }
-
-  std::string line;
-  while (getline(proc_keys_file, line)) {
-    std::vector<std::string> tokens = SplitBySpace(line);
-    if (tokens.size() < 9) {
-      continue;
-    }
-    std::string key_id = tokens[0];
-    std::string key_type = tokens[7];
-    // The key description may contain space.
-    std::string key_desc_prefix = tokens[8];
-    // The prefix has a ":" at the end
-    std::string key_desc_pattern = keyring_desc + ":";
-    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
-      continue;
-    }
-    *keyring_id = std::stoi(key_id, nullptr, 16);
-    return true;
-  }
-  return false;
-}
-
 static void Usage(int exit_code) {
-  fprintf(stderr, "usage: mini-keyctl -c PATHS -s DESCRIPTION\n");
-  fprintf(stderr, "\n");
-  fprintf(stderr, "-c, --cert_dirs     the certificate locations, separated by comma\n");
-  fprintf(stderr, "-k, --keyring       the keyring description\n");
+  fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
+  fprintf(stderr, "       mini-keyctl add <type> <desc> <data> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl padd <type> <desc> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl dadd <type> <desc_prefix> <cert_dir> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl unlink <key> <keyring>\n");
+  fprintf(stderr, "       mini-keyctl restrict_keyring <keyring>\n");
   _exit(exit_code);
 }
 
-int main(int argc, char** argv) {
-  if (argc < 5) Usage(1);
+int main(int argc, const char** argv) {
+  if (argc < 2) Usage(1);
+  const std::string action = argv[1];
 
-  std::string arg_cert_dirs;
-  std::string arg_keyring_desc;
-
-  for (int i = 1; i < argc; i++) {
-    std::string option = argv[i];
-    if (option == "-c" || option == "--cert_dirs") {
-      if (i + 1 < argc) arg_cert_dirs = argv[++i];
-    } else if (option == "-k" || option == "--keyring") {
-      if (i + 1 < argc) arg_keyring_desc = argv[++i];
-    }
-  }
-
-  if (arg_cert_dirs.empty() || arg_keyring_desc.empty()) {
-    LOG(ERROR) << "Missing cert_dirs or keyring desc";
+  if (action == "add") {
+    if (argc != 6) Usage(1);
+    std::string type = argv[2];
+    std::string desc = argv[3];
+    std::string data = argv[4];
+    std::string keyring = argv[5];
+    return Add(type, desc, data, keyring);
+  } else if (action == "dadd") {
+    if (argc != 6) Usage(1);
+    std::string type = argv[2];
+    // The key description contains desc_prefix and an index.
+    std::string desc_prefix = argv[3];
+    std::string cert_dir = argv[4];
+    std::string keyring = argv[5];
+    return AddCertsFromDir(type, desc_prefix, cert_dir, keyring);
+  } else if (action == "padd") {
+    if (argc != 5) Usage(1);
+    std::string type = argv[2];
+    std::string desc = argv[3];
+    std::string keyring = argv[4];
+    return Padd(type, desc, keyring);
+  } else if (action == "restrict_keyring") {
+    if (argc != 3) Usage(1);
+    std::string keyring = argv[2];
+    return RestrictKeyring(keyring);
+  } else if (action == "unlink") {
+    if (argc != 4) Usage(1);
+    key_serial_t key = std::stoi(argv[2], nullptr, 16);
+    const std::string keyring = argv[3];
+    return Unlink(key, keyring);
+  } else {
     Usage(1);
   }
 
-  // Get the keyring id
-  key_serial_t key_ring_id;
-  if (!GetKeyringId(arg_keyring_desc, &key_ring_id)) {
-    PLOG(ERROR) << "Can't find keyring with " << arg_keyring_desc;
-    return 1;
-  }
-
-  std::vector<std::string> cert_dirs = android::base::Split(arg_cert_dirs, ",");
-  int start_index = 0;
-  for (const auto& cert_dir : cert_dirs) {
-    int keys_added = AddKeys(cert_dir, key_ring_id, arg_keyring_desc, start_index);
-    start_index += keys_added;
-  }
-
-  // Prevent new keys to be added.
-  if (!android::base::GetBoolProperty("ro.debuggable", false) &&
-      keyctl_restrict_keyring(key_ring_id, nullptr, nullptr) < 0) {
-    PLOG(ERROR) << "Failed to restrict key ring " << arg_keyring_desc;
-    return 1;
-  }
-
   return 0;
 }
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..c4fc96c
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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 <mini_keyctl_utils.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <keyutils.h>
+
+static constexpr int kMaxCertSize = 4096;
+
+std::vector<std::string> SplitBySpace(const std::string& s) {
+  std::istringstream iss(s);
+  return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+                                  std::istream_iterator<std::string>{}};
+}
+
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+                    const std::string& cert_dir, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(cert_dir.c_str()), closedir);
+  if (!dir) {
+    PLOG(WARNING) << "Failed to open directory " << cert_dir;
+    return 1;
+  }
+  int keys_added = 0;
+  struct dirent* dp;
+  while ((dp = readdir(dir.get())) != NULL) {
+    if (dp->d_type != DT_REG) {
+      continue;
+    }
+    std::string cert_path = cert_dir + "/" + dp->d_name;
+    std::string cert_buf;
+    if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
+      LOG(ERROR) << "Failed to read " << cert_path;
+      continue;
+    }
+
+    if (cert_buf.size() > kMaxCertSize) {
+      LOG(ERROR) << "Certficate size too large: " << cert_path;
+      continue;
+    }
+
+    // Add key to keyring.
+    int key_desc_index = keys_added;
+    std::string key_desc = desc_prefix + std::to_string(key_desc_index);
+    key_serial_t key =
+        add_key(type.c_str(), key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
+    if (key < 0) {
+      PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
+      continue;
+    }
+    LOG(INFO) << "Key " << cert_path << " added to " << keyring << " with key id 0x" << std::hex
+              << key;
+    keys_added++;
+  }
+  return 0;
+}
+
+bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
+  if (!keyring_id) {
+    LOG(ERROR) << "keyring_id is null";
+    return false;
+  }
+
+  // If the keyring id is already a hex number, directly convert it to keyring id
+  try {
+    key_serial_t id = std::stoi(keyring_desc, nullptr, 16);
+    *keyring_id = id;
+    return true;
+  } catch (const std::exception& e) {
+    LOG(INFO) << "search /proc/keys for keyring id";
+  }
+
+  // Only keys allowed by SELinux rules will be shown here.
+  std::ifstream proc_keys_file("/proc/keys");
+  if (!proc_keys_file.is_open()) {
+    PLOG(ERROR) << "Failed to open /proc/keys";
+    return false;
+  }
+
+  std::string line;
+  while (getline(proc_keys_file, line)) {
+    std::vector<std::string> tokens = SplitBySpace(line);
+    if (tokens.size() < 9) {
+      continue;
+    }
+    std::string key_id = tokens[0];
+    std::string key_type = tokens[7];
+    // The key description may contain space.
+    std::string key_desc_prefix = tokens[8];
+    // The prefix has a ":" at the end
+    std::string key_desc_pattern = keyring_desc + ":";
+    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+      continue;
+    }
+    *keyring_id = std::stoi(key_id, nullptr, 16);
+    return true;
+  }
+  return false;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can't find keyring " << keyring;
+    return 1;
+  }
+
+  if (keyctl_unlink(key, keyring_id) < 0) {
+    PLOG(ERROR) << "Failed to unlink key 0x" << std::hex << key << " from keyring " << keyring_id;
+    return 1;
+  }
+  return 0;
+}
+
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+        const std::string& keyring) {
+  if (data.size() > kMaxCertSize) {
+    LOG(ERROR) << "Certificate too large";
+    return 1;
+  }
+
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+  if (key < 0) {
+    PLOG(ERROR) << "Failed to add key";
+    return 1;
+  }
+
+  LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+  return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  // read from stdin to get the certificates
+  std::istreambuf_iterator<char> begin(std::cin), end;
+  std::string data(begin, end);
+
+  if (data.size() > kMaxCertSize) {
+    LOG(ERROR) << "Certificate too large";
+    return 1;
+  }
+
+  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+  if (key < 0) {
+    PLOG(ERROR) << "Failed to add key";
+    return 1;
+  }
+
+  LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+  return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Cannot find keyring id";
+    return 1;
+  }
+
+  if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+    PLOG(ERROR) << "Cannot restrict keyring " << keyring;
+    return 1;
+  }
+  return 0;
+}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
new file mode 100644
index 0000000..3c69611
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -0,0 +1,48 @@
+/*
+ * 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 "include/keyutils.h"
+
+#include <string>
+
+// Add all files in a directory as certificates to a keyring. |keyring| could be the keyring
+// description or keyring id in hex.
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+                    const std::string& cert_dir, const std::string& keyring);
+
+// Add all the certs from directory path to keyring with keyring_id. Returns the number of keys
+// added. Returns non-zero if any error happens.
+int AddKeys(const std::string& path, const key_serial_t keyring_id, const std::string& type,
+            const std::string& desc, int start_index);
+
+// Add key to a keyring. Returns non-zero if error happens.
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+        const std::string& keyring);
+
+// Add key from stdin to a keyring. Returns non-zero if error happens.
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring);
+
+// Removes the link from a keyring to a key if exists. Return non-zero if error happens.
+int Unlink(key_serial_t key, const std::string& keyring);
+
+// Apply key-linking to a keyring. Return non-zero if error happens.
+int RestrictKeyring(const std::string& keyring);
+
+// Find the keyring id. Because request_key(2) syscall is not available or the key is
+// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
+// information in the descritption section depending on the key type, only the first word in the
+// keyring description is used for searching.
+bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id);
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 320b851..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -6,7 +6,6 @@
     vndk: {
         enabled: true,
     },
-    double_loadable: true,
     srcs: ["memtrack.cpp"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 260f655..51fb875 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -36,14 +36,9 @@
 __attribute__((visibility("default")))
 void InitializeNativeLoader();
 
-__attribute__((visibility("default")))
-jstring CreateClassLoaderNamespace(JNIEnv* env,
-                                   int32_t target_sdk_version,
-                                   jobject class_loader,
-                                   bool is_shared,
-                                   bool is_for_vendor,
-                                   jstring library_path,
-                                   jstring permitted_path);
+__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
+    JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
+    jstring library_path, jstring permitted_path);
 
 __attribute__((visibility("default"))) void* OpenNativeLibrary(
     JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 043f038..1c2581f 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -31,6 +31,7 @@
 #include <list>
 #include <memory>
 #include <mutex>
+#include <regex>
 #include <string>
 #include <vector>
 
@@ -140,10 +141,24 @@
 
 #if defined(__LP64__)
 static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+static constexpr const char* kVendorLibPath = "/vendor/lib64";
+static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
 #else
 static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+static constexpr const char* kVendorLibPath = "/vendor/lib";
+static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
 #endif
 
+static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+static const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+  APK_ORIGIN_DEFAULT = 0,
+  APK_ORIGIN_VENDOR = 1,
+  APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
 static bool is_debuggable() {
   bool debuggable = false;
 #ifdef __BIONIC__
@@ -179,7 +194,7 @@
   LibraryNamespaces() : initialized_(false) { }
 
   NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
-                                bool is_shared, bool is_for_vendor, jstring java_library_path,
+                                bool is_shared, jstring dex_path, jstring java_library_path,
                                 jstring java_permitted_path, std::string* error_msg) {
     std::string library_path; // empty string by default.
 
@@ -188,6 +203,8 @@
       library_path = library_path_utf_chars.c_str();
     }
 
+    ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
     // (http://b/27588281) This is a workaround for apps using custom
     // classloaders and calling System.load() with an absolute path which
     // is outside of the classloader library search path.
@@ -234,31 +251,50 @@
     std::string system_exposed_libraries = system_public_libraries_;
     const char* namespace_name = kClassloaderNamespaceName;
     android_namespace_t* vndk_ns = nullptr;
-    if (is_for_vendor && !is_shared) {
-      LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture");
+    if ((apk_origin == APK_ORIGIN_VENDOR ||
+         (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+        !is_shared) {
+      LOG_FATAL_IF(is_native_bridge,
+                   "Unbundled vendor / product apk must not use translated architecture");
 
-      // For vendor apks, give access to the vendor lib even though
+      // For vendor / product apks, give access to the vendor / product lib even though
       // they are treated as unbundled; the libs and apks are still bundled
-      // together in the vendor partition.
-#if defined(__LP64__)
-      std::string vendor_lib_path = "/vendor/lib64";
-#else
-      std::string vendor_lib_path = "/vendor/lib";
-#endif
-      library_path = library_path + ":" + vendor_lib_path.c_str();
-      permitted_path = permitted_path + ":" + vendor_lib_path.c_str();
+      // together in the vendor / product partition.
+      const char* origin_partition;
+      const char* origin_lib_path;
+
+      switch (apk_origin) {
+        case APK_ORIGIN_VENDOR:
+          origin_partition = "vendor";
+          origin_lib_path = kVendorLibPath;
+          break;
+        case APK_ORIGIN_PRODUCT:
+          origin_partition = "product";
+          origin_lib_path = kProductLibPath;
+          break;
+        default:
+          origin_partition = "unknown";
+          origin_lib_path = "";
+      }
+
+      LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
+                   origin_partition);
+
+      library_path = library_path + ":" + origin_lib_path;
+      permitted_path = permitted_path + ":" + origin_lib_path;
 
       // Also give access to LLNDK libraries since they are available to vendors
       system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
 
       // Give access to VNDK-SP libraries from the 'vndk' namespace.
       vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
-      LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr,
-                          "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName);
+      LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr, "Cannot find \"%s\" namespace for %s apks",
+                          kVndkNamespaceName, origin_partition);
 
       // Different name is useful for debugging
       namespace_name = kVendorClassloaderNamespaceName;
-      ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
+      ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+            origin_partition, library_path.c_str());
     } else {
       // oem and product public libraries are NOT available to vendor apks, otherwise it
       // would be system->vendor violation.
@@ -660,6 +696,28 @@
     return nullptr;
   }
 
+  ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+    ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+    if (dex_path != nullptr) {
+      ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+      if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+        apk_origin = APK_ORIGIN_VENDOR;
+      }
+
+      if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+        LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+                            "Dex path contains both vendor and product partition : %s",
+                            dex_path_utf_chars.c_str());
+
+        apk_origin = APK_ORIGIN_PRODUCT;
+      }
+    }
+
+    return apk_origin;
+  }
+
   bool initialized_;
   std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string system_public_libraries_;
@@ -690,31 +748,20 @@
 #endif
 }
 
-jstring CreateClassLoaderNamespace(JNIEnv* env,
-                                   int32_t target_sdk_version,
-                                   jobject class_loader,
-                                   bool is_shared,
-                                   bool is_for_vendor,
-                                   jstring library_path,
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+                                   bool is_shared, jstring dex_path, jstring library_path,
                                    jstring permitted_path) {
 #if defined(__ANDROID__)
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 
   std::string error_msg;
-  bool success = g_namespaces->Create(env,
-                                      target_sdk_version,
-                                      class_loader,
-                                      is_shared,
-                                      is_for_vendor,
-                                      library_path,
-                                      permitted_path,
-                                      &error_msg) != nullptr;
+  bool success = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
+                                      library_path, permitted_path, &error_msg) != nullptr;
   if (!success) {
     return env->NewStringUTF(error_msg.c_str());
   }
 #else
-  UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
-         library_path, permitted_path);
+  UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
 #endif
   return nullptr;
 }
@@ -779,8 +826,7 @@
     // In this case we create an isolated not-shared namespace for it.
     std::string create_error_msg;
     if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
-                                   false /* is_for_vendor */, library_path, nullptr,
-                                   &create_error_msg)) == nullptr) {
+                                   nullptr, library_path, nullptr, &create_error_msg)) == nullptr) {
       *error_msg = strdup(create_error_msg.c_str());
       return nullptr;
     }
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
index 11ecc43..2eb1203 100644
--- a/libnativeloader/native_loader_lazy.cpp
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -50,10 +50,10 @@
 }
 
 jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
-                                   bool is_shared, bool is_for_vendor, jstring library_path,
+                                   bool is_shared, jstring dex_path, jstring library_path,
                                    jstring permitted_path) {
   static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
-  return f(env, target_sdk_version, class_loader, is_shared, is_for_vendor, library_path,
+  return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
            permitted_path);
 }
 
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 26d0754..9951621 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -117,7 +117,7 @@
     std::string json_doc;
 
     if (!android::base::ReadFileToString(file_name, &json_doc)) {
-        LOG(ERROR) << "Failed to read task profiles from " << file_name;
+        PLOG(ERROR) << "Failed to read task profiles from " << file_name;
         return false;
     }
 
@@ -185,7 +185,7 @@
 
     // mkdir <path> [mode] [owner] [group]
     if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
-        PLOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
         return false;
     }
 
@@ -289,7 +289,7 @@
     std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
     std::string content;
     if (!android::base::ReadFileToString(file_name, &content)) {
-        LOG(ERROR) << "Failed to read " << file_name;
+        PLOG(ERROR) << "Failed to read " << file_name;
         return false;
     }
 
@@ -323,7 +323,7 @@
 
 CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
     if (!LoadRcFile()) {
-        PLOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
+        LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
     }
 }
 
@@ -360,27 +360,42 @@
         return false;
     }
 
-    cg_file_size_ = sb.st_size;
-    if (cg_file_size_ < sizeof(CgroupFile)) {
-        PLOG(ERROR) << "Invalid file format " << cgroup_rc_path;
+    size_t file_size = sb.st_size;
+    if (file_size < sizeof(CgroupFile)) {
+        LOG(ERROR) << "Invalid file format " << cgroup_rc_path;
         return false;
     }
 
-    cg_file_data_ = (CgroupFile*)mmap(nullptr, cg_file_size_, PROT_READ, MAP_SHARED, fd, 0);
-    if (cg_file_data_ == MAP_FAILED) {
+    CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+    if (file_data == MAP_FAILED) {
         PLOG(ERROR) << "Failed to mmap " << cgroup_rc_path;
         return false;
     }
 
-    if (cg_file_data_->version_ != CgroupFile::FILE_CURR_VERSION) {
-        PLOG(ERROR) << cgroup_rc_path << " file version mismatch";
+    if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+        LOG(ERROR) << cgroup_rc_path << " file version mismatch";
+        munmap(file_data, file_size);
         return false;
     }
 
+    if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
+        LOG(ERROR) << cgroup_rc_path << " file has invalid size";
+        munmap(file_data, file_size);
+        return false;
+    }
+
+    cg_file_data_ = file_data;
+    cg_file_size_ = file_size;
+
     return true;
 }
 
-void CgroupMap::Print() {
+void CgroupMap::Print() const {
+    if (!cg_file_data_) {
+        LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
+                   << "] failed, RC file was not initialized properly";
+        return;
+    }
     LOG(INFO) << "File version = " << cg_file_data_->version_;
     LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
 
@@ -397,7 +412,7 @@
 
     // load cgroups.json file
     if (!ReadDescriptors(&descriptors)) {
-        PLOG(ERROR) << "Failed to load cgroup description file";
+        LOG(ERROR) << "Failed to load cgroup description file";
         return false;
     }
 
@@ -412,7 +427,7 @@
 
     // mkdir <CGROUPS_RC_DIR> 0711 system system
     if (!Mkdir(CGROUPS_RC_DIR, 0711, "system", "system")) {
-        PLOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
+        LOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
         return false;
     }
 
@@ -428,7 +443,7 @@
     std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CGROUPS_RC_FILE);
     // chmod 0644 <cgroup_rc_path>
     if (fchmodat(AT_FDCWD, cgroup_rc_path.c_str(), 0644, AT_SYMLINK_NOFOLLOW) < 0) {
-        LOG(ERROR) << "fchmodat() failed";
+        PLOG(ERROR) << "fchmodat() failed";
         return false;
     }
 
@@ -437,6 +452,8 @@
 
 const CgroupController* CgroupMap::FindController(const std::string& name) const {
     if (!cg_file_data_) {
+        LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
+                   << "] failed, RC file was not initialized properly";
         return nullptr;
     }
 
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index ba2caf7..1c355cd 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -92,5 +92,5 @@
     ~CgroupMap();
 
     bool LoadRcFile();
-    void Print();
+    void Print() const;
 };
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 026c011..1eefada 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -167,7 +167,7 @@
     if (!controller) return -1;
 
     if (!controller->GetTaskGroup(tid, &subgroup)) {
-        PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
         return -1;
     }
     return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index fded417..dcf53f9 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -119,7 +119,7 @@
     std::string path;
 
     if (!attribute_->GetPathForTask(tid, &path)) {
-        PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
         return false;
     }
 
@@ -174,7 +174,7 @@
     if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
         // If the thread is in the process of exiting, don't flag an error
         if (errno != ESRCH) {
-            PLOG(ERROR) << "JoinGroup failed to write '" << value << "'; fd=" << fd;
+            PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
             return false;
         }
     }
@@ -187,7 +187,7 @@
     if (fd_ >= 0) {
         // fd is cached, reuse it
         if (!AddTidToCgroup(pid, fd_)) {
-            PLOG(ERROR) << "Failed to add task into cgroup";
+            LOG(ERROR) << "Failed to add task into cgroup";
             return false;
         }
         return true;
@@ -206,7 +206,7 @@
         return false;
     }
     if (!AddTidToCgroup(pid, tmp_fd)) {
-        PLOG(ERROR) << "Failed to add task into cgroup";
+        LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
 
@@ -219,7 +219,7 @@
         return true;
     }
     if (!AddTidToCgroup(pid, tmp_fd)) {
-        PLOG(ERROR) << "Failed to add task into cgroup";
+        LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
 
@@ -232,7 +232,7 @@
     if (fd_ >= 0) {
         // fd is cached, reuse it
         if (!AddTidToCgroup(tid, fd_)) {
-            PLOG(ERROR) << "Failed to add task into cgroup";
+            LOG(ERROR) << "Failed to add task into cgroup";
             return false;
         }
         return true;
@@ -244,7 +244,7 @@
     }
 
     // application-dependent path can't be used with tid
-    PLOG(ERROR) << "Application profile can't be applied to a thread";
+    LOG(ERROR) << "Application profile can't be applied to a thread";
     return false;
 #else
     std::string tasks_path = controller_->GetTasksFilePath(path_);
@@ -254,7 +254,7 @@
         return true;
     }
     if (!AddTidToCgroup(tid, tmp_fd)) {
-        PLOG(ERROR) << "Failed to add task into cgroup";
+        LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
 
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 27cda93..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -31,7 +31,6 @@
     vndk: {
         enabled: true,
     },
-    double_loadable: true,
     host_supported: true,
     srcs: [
         "process.cpp",
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/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dba41d1..b3beb6e 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -101,7 +101,7 @@
 
   size_t Size() { return size_; }
 
-  void Clear();
+  void Clear() override;
 
  protected:
   size_t size_ = 0;
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 878ced3..1c2a81c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -18,6 +18,7 @@
 #define _LIBUNWINDSTACK_REGS_H
 
 #include <stdint.h>
+#include <unistd.h>
 
 #include <functional>
 #include <string>
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/libusbhost/Android.bp b/libusbhost/Android.bp
index 39bf3a5..fc6f305 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -20,7 +20,6 @@
     vndk: {
         enabled: true,
     },
-    double_loadable: true,
     host_supported: true,
     srcs: ["usbhost.c"],
     cflags: ["-Werror"],
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 2e0cf6e..b594ca5 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -45,6 +45,44 @@
     bool* mDeleted;
 };
 
+// A version of Foo that ensures that all objects are allocated at the same
+// address. No more than one can be allocated at a time. Thread-hostile.
+class FooFixedAlloc : public RefBase {
+public:
+    static void* operator new(size_t size) {
+        if (mAllocCount != 0) {
+            abort();
+        }
+        mAllocCount = 1;
+        if (theMemory == nullptr) {
+            theMemory = malloc(size);
+        }
+        return theMemory;
+    }
+
+    static void operator delete(void *p) {
+        if (mAllocCount != 1 || p != theMemory) {
+            abort();
+        }
+        mAllocCount = 0;
+    }
+
+    FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) {
+        *mDeleted = false;
+    }
+
+    ~FooFixedAlloc() {
+        *mDeleted = true;
+    }
+private:
+    bool* mDeleted;
+    static int mAllocCount;
+    static void* theMemory;
+};
+
+int FooFixedAlloc::mAllocCount(0);
+void* FooFixedAlloc::theMemory(nullptr);
+
 TEST(RefBase, StrongMoves) {
     bool isDeleted;
     Foo* foo = new Foo(&isDeleted);
@@ -90,6 +128,91 @@
     ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
 }
 
+TEST(RefBase, Comparisons) {
+    bool isDeleted, isDeleted2;
+    Foo* foo = new Foo(&isDeleted);
+    Foo* foo2 = new Foo(&isDeleted2);
+    sp<Foo> sp1(foo);
+    sp<Foo> sp2(foo2);
+    wp<Foo> wp1(sp1);
+    wp<Foo> wp2(sp1);
+    wp<Foo> wp3(sp2);
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_TRUE(wp1 == sp1);
+    ASSERT_TRUE(wp3 == sp2);
+    ASSERT_TRUE(wp1 != sp2);
+    ASSERT_TRUE(wp1 <= wp2);
+    ASSERT_TRUE(wp1 >= wp2);
+    ASSERT_FALSE(wp1 != wp2);
+    ASSERT_FALSE(wp1 > wp2);
+    ASSERT_FALSE(wp1 < wp2);
+    ASSERT_FALSE(sp1 == sp2);
+    ASSERT_TRUE(sp1 != sp2);
+    bool sp1_smaller = sp1 < sp2;
+    wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3;
+    wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1;
+    ASSERT_TRUE(wp_smaller < wp_larger);
+    ASSERT_TRUE(wp_smaller != wp_larger);
+    ASSERT_TRUE(wp_smaller <= wp_larger);
+    ASSERT_FALSE(wp_smaller == wp_larger);
+    ASSERT_FALSE(wp_smaller > wp_larger);
+    ASSERT_FALSE(wp_smaller >= wp_larger);
+    sp2 = nullptr;
+    ASSERT_TRUE(isDeleted2);
+    ASSERT_FALSE(isDeleted);
+    ASSERT_FALSE(wp3 == sp2);
+    // Comparison results on weak pointers should not be affected.
+    ASSERT_TRUE(wp_smaller < wp_larger);
+    ASSERT_TRUE(wp_smaller != wp_larger);
+    ASSERT_TRUE(wp_smaller <= wp_larger);
+    ASSERT_FALSE(wp_smaller == wp_larger);
+    ASSERT_FALSE(wp_smaller > wp_larger);
+    ASSERT_FALSE(wp_smaller >= wp_larger);
+    wp2 = nullptr;
+    ASSERT_FALSE(wp1 == wp2);
+    ASSERT_TRUE(wp1 != wp2);
+    wp1.clear();
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_FALSE(wp1 != wp2);
+    wp3.clear();
+    ASSERT_TRUE(wp1 == wp3);
+    ASSERT_FALSE(wp1 != wp3);
+    ASSERT_FALSE(isDeleted);
+    sp1.clear();
+    ASSERT_TRUE(isDeleted);
+    ASSERT_TRUE(sp1 == sp2);
+}
+
+// Check whether comparison against dead wp works, even if the object referenced
+// by the new wp happens to be at the same address.
+TEST(RefBase, ReplacedComparison) {
+    bool isDeleted, isDeleted2;
+    FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted);
+    sp<FooFixedAlloc> sp1(foo);
+    wp<FooFixedAlloc> wp1(sp1);
+    ASSERT_TRUE(wp1 == sp1);
+    sp1.clear();  // Deallocates the object.
+    ASSERT_TRUE(isDeleted);
+    FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2);
+    ASSERT_FALSE(isDeleted2);
+    ASSERT_EQ(foo, foo2);  // Not technically a legal comparison, but ...
+    sp<FooFixedAlloc> sp2(foo2);
+    wp<FooFixedAlloc> wp2(sp2);
+    ASSERT_TRUE(sp2 == wp2);
+    ASSERT_FALSE(sp2 != wp2);
+    ASSERT_TRUE(sp2 != wp1);
+    ASSERT_FALSE(sp2 == wp1);
+    ASSERT_FALSE(sp2 == sp1);  // sp1 is null.
+    ASSERT_FALSE(wp1 == wp2);  // wp1 refers to old object.
+    ASSERT_TRUE(wp1 != wp2);
+    ASSERT_TRUE(wp1 > wp2 || wp1 < wp2);
+    ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2);
+    ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2);
+    ASSERT_FALSE(wp1 == nullptr);
+    wp1 = sp2;
+    ASSERT_TRUE(wp1 == wp2);
+    ASSERT_FALSE(wp1 != wp2);
+}
 
 // Set up a situation in which we race with visit2AndRremove() to delete
 // 2 strong references.  Bar destructor checks that there are no early
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 1780cf2..730d631 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -171,6 +171,8 @@
 #define ANDROID_REF_BASE_H
 
 #include <atomic>
+#include <functional>
+#include <type_traits>  // for common_type.
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -192,19 +194,26 @@
 // ---------------------------------------------------------------------------
 
 #define COMPARE_WEAK(_op_)                                      \
-inline bool operator _op_ (const sp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const sp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
 template<typename U>                                            \
 inline bool operator _op_ (const U* o) const {                  \
     return m_ptr _op_ o;                                        \
+}                                                               \
+/* Needed to handle type inference for nullptr: */              \
+inline bool operator _op_ (const T* o) const {                  \
+    return m_ptr _op_ o;                                        \
+}
+
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _wp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_)                 \
+template<typename U>                                             \
+inline bool operator _op_ (const U* o) const {                   \
+    return _wp_compare_<_compare_>(m_ptr, o);                    \
 }
 
 // ---------------------------------------------------------------------------
@@ -395,39 +404,51 @@
 
     COMPARE_WEAK(==)
     COMPARE_WEAK(!=)
-    COMPARE_WEAK(>)
-    COMPARE_WEAK(<)
-    COMPARE_WEAK(<=)
-    COMPARE_WEAK(>=)
+    COMPARE_WEAK_FUNCTIONAL(>, std::greater)
+    COMPARE_WEAK_FUNCTIONAL(<, std::less)
+    COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
+    COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
 
-    inline bool operator == (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
-    }
     template<typename U>
     inline bool operator == (const wp<U>& o) const {
-        return m_ptr == o.m_ptr;
+        return m_refs == o.m_refs;  // Implies m_ptr == o.mptr; see invariants below.
     }
 
-    inline bool operator > (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+    template<typename U>
+    inline bool operator == (const sp<U>& o) const {
+        // Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
+        // object at the same address.
+        if (o == nullptr) {
+          return m_ptr == nullptr;
+        } else {
+          return m_refs == o->getWeakRefs();  // Implies m_ptr == o.mptr.
+        }
     }
+
+    template<typename U>
+    inline bool operator != (const sp<U>& o) const {
+        return !(*this == o);
+    }
+
     template<typename U>
     inline bool operator > (const wp<U>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+        if (m_ptr == o.m_ptr) {
+            return _wp_compare_<std::greater>(m_refs, o.m_refs);
+        } else {
+            return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
+        }
     }
 
-    inline bool operator < (const wp<T>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
-    }
     template<typename U>
     inline bool operator < (const wp<U>& o) const {
-        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+        if (m_ptr == o.m_ptr) {
+            return _wp_compare_<std::less>(m_refs, o.m_refs);
+        } else {
+            return _wp_compare_<std::less>(m_ptr, o.m_ptr);
+        }
     }
-                         inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
     template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
-                         inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
     template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
-                         inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
     template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
 
 private:
@@ -446,6 +467,22 @@
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
+// Implementation invariants:
+// Either
+// 1) m_ptr and m_refs are both null, or
+// 2) m_refs == m_ptr->mRefs, or
+// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
+//    to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
+//
+// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
+// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
+// null or point to the same object. If two wp's have identical m_ptr fields, they either both
+// point to the same live object and thus have the same m_ref fields, or at least one of the
+// objects is no longer live.
+//
+// Note that the above comparison operations go out of their way to provide an ordering consistent
+// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
+
 template<typename T>
 wp<T>::wp(T* other)
     : m_ptr(other)
@@ -595,6 +632,7 @@
 {
     if (m_ptr) {
         m_refs->decWeak(this);
+        m_refs = 0;
         m_ptr = 0;
     }
 }
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 1571129..9cd7c75 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,6 +17,9 @@
 #ifndef ANDROID_STRONG_POINTER_H
 #define ANDROID_STRONG_POINTER_H
 
+#include <functional>
+#include <type_traits>  // for common_type.
+
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -24,13 +27,12 @@
 
 // ---------------------------------------------------------------------------
 
-#define COMPARE(_op_)                                           \
-inline bool operator _op_ (const sp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
+// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
+// was created before the sp<>, and they point to different objects, they may
+// compare equal even if they are entirely unrelated. E.g. CameraService
+// currently performa such comparisons.
+
+#define COMPARE_STRONG(_op_)                                           \
 template<typename U>                                            \
 inline bool operator _op_ (const sp<U>& o) const {              \
     return m_ptr _op_ o.m_ptr;                                  \
@@ -39,14 +41,27 @@
 inline bool operator _op_ (const U* o) const {                  \
     return m_ptr _op_ o;                                        \
 }                                                               \
-inline bool operator _op_ (const wp<T>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const wp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
+/* Needed to handle type inference for nullptr: */              \
+inline bool operator _op_ (const T* o) const {                  \
+    return m_ptr _op_ o;                                        \
 }
 
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)               \
+template<typename U>                                             \
+inline bool operator _op_ (const sp<U>& o) const {               \
+    return _sp_compare_<_compare_>(m_ptr, o.m_ptr);              \
+}                                                                \
+template<typename U>                                             \
+inline bool operator _op_ (const U* o) const {                   \
+    return _sp_compare_<_compare_>(m_ptr, o);                    \
+}
 // ---------------------------------------------------------------------------
 
 template<typename T>
@@ -89,12 +104,23 @@
 
     // Operators
 
-    COMPARE(==)
-    COMPARE(!=)
-    COMPARE(>)
-    COMPARE(<)
-    COMPARE(<=)
-    COMPARE(>=)
+    COMPARE_STRONG(==)
+    COMPARE_STRONG(!=)
+    COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+    COMPARE_STRONG_FUNCTIONAL(<, std::less)
+    COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+    COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
+
+    // Punt these to the wp<> implementation.
+    template<typename U>
+    inline bool operator == (const wp<U>& o) const {
+        return o == *this;
+    }
+
+    template<typename U>
+    inline bool operator != (const wp<U>& o) const {
+        return o != *this;
+    }
 
 private:    
     template<typename Y> friend class sp;
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..789bf5e 100755
--- a/mkbootimg/unpack_bootimg.py
+++ b/mkbootimg/unpack_bootimg.py
@@ -48,12 +48,12 @@
     print('boot_magic: %s' % boot_magic)
     kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
     print('kernel_size: %s' % kernel_ramdisk_second_info[0])
-    print('kernel load address: %s' % kernel_ramdisk_second_info[1])
+    print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
     print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
-    print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
+    print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
     print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
-    print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
-    print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
+    print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
+    print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
     print('page size: %s' % kernel_ramdisk_second_info[7])
     print('boot image header version: %s' % kernel_ramdisk_second_info[8])
     print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
@@ -77,7 +77,7 @@
         recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
         print('recovery dtbo size: %s' % recovery_dtbo_size)
         recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
-        print('recovery dtbo offset: %s' % recovery_dtbo_offset)
+        print('recovery dtbo offset: %#x' % recovery_dtbo_offset)
         boot_header_size = unpack('I', args.boot_img.read(4))[0]
         print('boot header size: %s' % boot_header_size)
     else:
@@ -86,7 +86,7 @@
         dtb_size = unpack('I', args.boot_img.read(4))[0]
         print('dtb size: %s' % dtb_size)
         dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
-        print('dtb address: %s' % dtb_load_address)
+        print('dtb address: %#x' % dtb_load_address)
     else:
         dtb_size = 0
 
@@ -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.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 0cde3f2..d4d5c28 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -40,7 +40,7 @@
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
@@ -94,11 +94,7 @@
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
 namespace.media.links = default
-namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.media.link.default.shared_libs += libandroid.so
-namespace.media.link.default.shared_libs += libbinder_ndk.so
-namespace.media.link.default.shared_libs += libmediametrics.so
-namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.media.link.default.allow_all_shared_libs = true
 
 ###############################################################################
 # "conscrypt" APEX namespace
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 4b2dd07..552d685 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -125,7 +125,7 @@
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 54f4c98..beb6125 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -66,7 +66,7 @@
 # ld.config.txt.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ce4b380..b769b94 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
@@ -397,6 +387,7 @@
     restorecon_recursive /metadata
     mkdir /metadata/vold
     chmod 0700 /metadata/vold
+    mkdir /metadata/password_slots 0771 root system
 
 on late-fs
     # Ensure that tracefs has the correct permissions.
@@ -425,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
@@ -543,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
@@ -581,21 +574,22 @@
     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.
     restorecon --recursive --skip-ce /data
 
     # 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
+    exec -- /system/bin/mini-keyctl dadd asymmetric product_cert /product/etc/security/cacerts_fsverity .fs-verity
+    exec -- /system/bin/mini-keyctl dadd asymmetric vendor_cert /vendor/etc/security/cacerts_fsverity .fs-verity
+    # Prevent future key links to fsverity keyring
+    exec -- /system/bin/mini-keyctl restrict_keyring .fs-verity
 
     # 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/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 41263e5..c61f7d0 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,38 @@
 #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 enum dev_type dev_type = MMC_RPMB;
 
-static void show_usage_and_exit(int code)
-{
-    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+static enum dev_type parse_dev_type(const char* dev_type_name) {
+    if (!strcmp(dev_type_name, "mmc")) {
+        return MMC_RPMB;
+    } else if (!strcmp(dev_type_name, "virt")) {
+        return VIRT_RPMB;
+    } else {
+        return UNKNOWN_RPMB;
+    }
+}
+
+static const char* _sopts = "hp:d:r:t:";
+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'},
+                                       {"dev_type", required_argument, NULL, 't'},
+                                       {0, 0, 0, 0}};
+
+static void show_usage_and_exit(int code) {
+    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+    ALOGE("Available dev types: mmc, virt\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 +105,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 +127,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 +172,58 @@
     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;
+            case 't':
+                dev_type = parse_dev_type(optarg);
+                if (dev_type == UNKNOWN_RPMB) {
+                    ALOGE("Unrecognized dev type: %s\n", optarg);
+                    show_usage_and_exit(EXIT_FAILURE);
+                }
+                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 +234,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;
+    rc = rpmb_open(rpmb_devname, dev_type);
+    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..29827e2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -51,17 +51,16 @@
 
 static int rpmb_fd = -1;
 static uint8_t read_buf[4096];
+static enum dev_type dev_type = UNKNOWN_RPMB;
 
 #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,41 +69,16 @@
 
 #endif
 
-
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
-{
-    int rc;
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
     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;
+    int rc;
 
-    if (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;
-    if (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);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_RELW;
         cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
         cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -121,12 +95,6 @@
     }
 
     if (req->write_size) {
-        if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
-            ALOGW("invalid write size %u\n", req->write_size);
-            msg->result = STORAGE_ERR_NOT_VALID;
-            goto err_response;
-        }
-
         cmd->write_flag = MMC_WRITE_FLAG_W;
         cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
         cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -143,17 +111,9 @@
     }
 
     if (req->read_size) {
-        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;
-        }
-
         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
@@ -163,15 +123,97 @@
         cmd++;
     }
 
-    rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+    rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
     if (rc < 0) {
         ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+    }
+    return rc;
+}
+
+static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
+                              size_t payload_size) {
+    int rc;
+    uint16_t res_count = read_size / MMC_BLOCK_SIZE;
+    uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;
+    rc = write(rpmb_fd, &res_count, sizeof(res_count));
+    if (rc < 0) {
+        return rc;
+    }
+    rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));
+    if (rc < 0) {
+        return rc;
+    }
+    rc = write(rpmb_fd, payload, payload_size);
+    if (rc < 0) {
+        return rc;
+    }
+    rc = read(rpmb_fd, read_buf, read_size);
+    return rc;
+}
+
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+    int rc;
+    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));
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    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);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+        ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+        ALOGW("invalid write size %u\n", req->write_size);
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    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;
+    }
+
+    if (dev_type == MMC_RPMB) {
+        rc = send_mmc_rpmb_req(rpmb_fd, req);
+        if (rc < 0) {
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+    } else if (dev_type == VIRT_RPMB) {
+        size_t payload_size = req->reliable_write_size + req->write_size;
+        rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
+        if (rc < 0) {
+            ALOGE("send_virt_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+        if (rc != req->read_size) {
+            ALOGE("send_virt_rpmb_req got incomplete response: "
+                  "(size %d, expected %d)\n",
+                  rc, req->read_size);
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
+    } else {
+        ALOGE("Unsupported dev_type\n");
         msg->result = STORAGE_ERR_GENERIC;
         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 +230,20 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int rpmb_open(const char *rpmb_devname)
-{
+int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
     int rc;
+    dev_type = open_dev_type;
 
     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..4c330c9 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,8 @@
 #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);
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+
+int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
 void rpmb_close(void);