Merge "Support 'fastboot getvar unlocked' command."
diff --git a/adb/Android.bp b/adb/Android.bp
index 6cff0be..bccc71a 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -349,10 +349,6 @@
         "libavb_user",
         "libdiagnose_usb",
         "libqemu_pipe",
-
-        // `daemon/shell_service.cpp` uses selinux_android_setcon(), which is not exposed by
-        // libselinux.
-        "libselinux",
     ],
 
     shared_libs: [
@@ -367,6 +363,7 @@
         "libfs_mgr",
         "liblog",
         "libmdnssd",
+        "libselinux",
     ],
 }
 
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 76500d4..380dfa6 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -146,6 +146,10 @@
         return true;
     }
     bool is_root = strcmp(dir, "/") == 0;
+    if (is_root && !find_mount("/system", false).empty()) {
+        dir = "/system";
+        is_root = false;
+    }
     std::string dev = find_mount(dir, is_root);
     // Even if the device for the root is not found, we still try to remount it
     // as rw. This typically only happens when running Android in a container:
@@ -209,24 +213,26 @@
     bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
     bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
 
-    std::vector<std::string> partitions{"/odm", "/oem", "/product_services", "/product", "/vendor"};
-    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
-        partitions.push_back("/");
-    } else {
-        partitions.push_back("/system");
-    }
+    std::vector<std::string> partitions{"/",        "/odm",   "/oem", "/product_services",
+                                        "/product", "/vendor"};
 
     bool verity_enabled = (system_verified || vendor_verified);
 
     // If we can use overlayfs, lets get it in place first
     // before we struggle with determining deduplication operations.
-    if (!verity_enabled && fs_mgr_overlayfs_setup() && fs_mgr_overlayfs_mount_all()) {
-        WriteFdExactly(fd.get(), "overlayfs mounted\n");
+    if (!verity_enabled && fs_mgr_overlayfs_setup()) {
+        std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                                   fs_mgr_free_fstab);
+        if (fs_mgr_overlayfs_mount_all(fstab.get())) {
+            WriteFdExactly(fd.get(), "overlayfs mounted\n");
+        }
     }
 
     // Find partitions that are deduplicated, and can be un-deduplicated.
     std::set<std::string> dedup;
-    for (const auto& partition : partitions) {
+    for (const auto& part : partitions) {
+        auto partition = part;
+        if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system";
         std::string dev = find_mount(partition.c_str(), partition == "/");
         if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
             continue;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 8417690..2bac486 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -103,7 +103,9 @@
     if (reboot_arg.empty()) reboot_arg = "adb";
     std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
 
-    if (reboot_arg == "fastboot" && access("/dev/socket/recovery", F_OK) == 0) {
+    if (reboot_arg == "fastboot" &&
+        android::base::GetBoolProperty("ro.boot.logical_partitions", false) &&
+        access("/dev/socket/recovery", F_OK) == 0) {
         LOG(INFO) << "Recovery specific reboot fastboot";
         /*
          * The socket is created to allow switching between recovery and
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 49cc0c9..1748665 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -170,7 +170,9 @@
 //
 //  In either case this macro has no effect on runtime behavior and performance
 //  of code.
+#ifndef FALLTHROUGH_INTENDED
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
+#endif
 
 // Current ABI string
 #if defined(__arm__)
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 93f7572..577e336 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -34,6 +34,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -282,6 +283,7 @@
   switch (crash_info->header.version) {
     case 2:
       *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+      FALLTHROUGH_INTENDED;
     case 1:
       *abort_msg_address = crash_info->data.v1.abort_msg_address;
       *siginfo = crash_info->data.v1.siginfo;
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0fe1d0..f0bdfbf 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -224,7 +224,7 @@
     // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
       char buf[1];
-      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+      UNUSED(TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf))));
       return do_action(arg + strlen("wait-"));
     } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
       errno = 0;
@@ -258,10 +258,14 @@
       __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
       __assert2("some_file.c", 123, "some_function", "false");
+#if !defined(__clang_analyzer__)
     } else if (!strcasecmp(arg, "fortify")) {
+      // FORTIFY is disabled when running clang-tidy and other tools, so this
+      // shouldn't depend on internal implementation details of it.
       char buf[10];
       __read_chk(-1, buf, 32, 10);
       while (true) pause();
+#endif
     } else if (!strcasecmp(arg, "fdsan_file")) {
       FILE* f = fopen("/dev/null", "r");
       close(fileno(f));
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e2ea480..bea8b43 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -37,6 +37,7 @@
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
@@ -587,9 +588,28 @@
     "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
 
 static pid_t seccomp_fork_impl(void (*prejail)()) {
-  unique_fd policy_fd(open(kDebuggerdSeccompPolicy, O_RDONLY | O_CLOEXEC));
-  if (policy_fd == -1) {
-    LOG(FATAL) << "failed to open policy " << kDebuggerdSeccompPolicy;
+  std::string policy;
+  if (!android::base::ReadFileToString(kDebuggerdSeccompPolicy, &policy)) {
+    PLOG(FATAL) << "failed to read policy file";
+  }
+
+  // Allow a bunch of syscalls used by the tests.
+  policy += "\nclone: 1";
+  policy += "\nsigaltstack: 1";
+  policy += "\nnanosleep: 1";
+
+  FILE* tmp_file = tmpfile();
+  if (!tmp_file) {
+    PLOG(FATAL) << "tmpfile failed";
+  }
+
+  unique_fd tmp_fd(dup(fileno(tmp_file)));
+  if (!android::base::WriteStringToFd(policy, tmp_fd.get())) {
+    PLOG(FATAL) << "failed to write policy to tmpfile";
+  }
+
+  if (lseek(tmp_fd.get(), 0, SEEK_SET) != 0) {
+    PLOG(FATAL) << "failed to seek tmp_fd";
   }
 
   ScopedMinijail jail{minijail_new()};
@@ -600,7 +620,7 @@
   minijail_no_new_privs(jail.get());
   minijail_log_seccomp_filter_failures(jail.get());
   minijail_use_seccomp_filter(jail.get());
-  minijail_parse_seccomp_filters_from_fd(jail.get(), policy_fd.release());
+  minijail_parse_seccomp_filters_from_fd(jail.get(), tmp_fd.release());
 
   pid_t result = fork();
   if (result == -1) {
@@ -735,6 +755,16 @@
   ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
 }
 
+extern "C" void foo() {
+  LOG(INFO) << "foo";
+  std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+  LOG(INFO) << "bar";
+  std::this_thread::sleep_for(1s);
+}
+
 TEST_F(CrasherTest, seccomp_backtrace) {
   int intercept_result;
   unique_fd output_fd;
@@ -742,6 +772,11 @@
   static const auto dump_type = kDebuggerdNativeBacktrace;
   StartProcess(
       []() {
+        std::thread a(foo);
+        std::thread b(bar);
+
+        std::this_thread::sleep_for(100ms);
+
         raise_debugger_signal(dump_type);
         _exit(0);
       },
@@ -756,6 +791,8 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+  ASSERT_BACKTRACE_FRAME(result, "foo");
+  ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
 TEST_F(CrasherTest, seccomp_crash_logcat) {
@@ -1017,3 +1054,42 @@
   ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
   ASSERT_STREQ("any", outbuf);
 }
+
+TEST(tombstoned, interceptless_backtrace) {
+  // Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones.
+  auto get_tombstone_timestamps = []() -> std::map<int, time_t> {
+    std::map<int, time_t> result;
+    for (int i = 0; i < 99; ++i) {
+      std::string path = android::base::StringPrintf("/data/tombstones/tombstone_%02d", i);
+      struct stat st;
+      if (stat(path.c_str(), &st) == 0) {
+        result[i] = st.st_mtim.tv_sec;
+      }
+    }
+    return result;
+  };
+
+  auto before = get_tombstone_timestamps();
+  for (int i = 0; i < 50; ++i) {
+    raise_debugger_signal(kDebuggerdNativeBacktrace);
+  }
+  auto after = get_tombstone_timestamps();
+
+  int diff = 0;
+  for (int i = 0; i < 99; ++i) {
+    if (after.count(i) == 0) {
+      continue;
+    }
+    if (before.count(i) == 0) {
+      ++diff;
+      continue;
+    }
+    if (before[i] != after[i]) {
+      ++diff;
+    }
+  }
+
+  // We can't be sure that nothing's crash looping in the background.
+  // This should be good enough, though...
+  ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 079a574..ed7423b 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -47,6 +47,7 @@
 #include <unwindstack/Regs.h>
 
 #include "debuggerd/handler.h"
+#include "handler/fallback.h"
 #include "tombstoned/tombstoned.h"
 #include "util.h"
 
@@ -187,7 +188,7 @@
 static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
   static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
 
-  if (info->si_value.sival_int == ~0) {
+  if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) {
     // Asked to dump by the original signal recipient.
     uint64_t val = trace_output.load();
     auto [tid, fd] = unpack_thread_fd(val);
@@ -259,7 +260,7 @@
 
         siginfo_t siginfo = {};
         siginfo.si_code = SI_QUEUE;
-        siginfo.si_value.sival_int = ~0;
+        siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;
         siginfo.si_pid = getpid();
         siginfo.si_uid = getuid();
 
@@ -331,7 +332,7 @@
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
                                            void* abort_message) {
-  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
+  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_ptr != nullptr) {
     return trace_handler(info, ucontext);
   } else {
     return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 15557b6..a064ca0 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -58,6 +58,8 @@
 #include "dump_type.h"
 #include "protocol.h"
 
+#include "handler/fallback.h"
+
 using android::base::Pipe;
 
 // We muck with our fds in a 'thread' that doesn't share the same fd table.
@@ -473,13 +475,15 @@
   }
 
   void* abort_message = nullptr;
+  uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
   if (signal_number == DEBUGGER_SIGNAL) {
     if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
       // Allow for the abort message to be explicitly specified via the sigqueue value.
       // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
-      uintptr_t value = reinterpret_cast<uintptr_t>(info->si_ptr);
-      abort_message = reinterpret_cast<void*>(value & ~1);
-      info->si_ptr = reinterpret_cast<void*>(value & 1);
+      if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {
+        abort_message = reinterpret_cast<void*>(si_val & ~1);
+        info->si_ptr = reinterpret_cast<void*>(si_val & 1);
+      }
     }
   } else {
     if (g_callbacks.get_abort_message) {
@@ -492,7 +496,8 @@
   // of a specific thread. It is possible that the prctl call might return 1,
   // then return 0 in subsequent calls, so check the sival_int to determine if
   // the fallback handler should be called first.
-  if (info->si_value.sival_int == ~0 || prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump ||
+      prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
     // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
diff --git a/debuggerd/handler/fallback.h b/debuggerd/handler/fallback.h
new file mode 100644
index 0000000..597f582
--- /dev/null
+++ b/debuggerd/handler/fallback.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+static void* const kDebuggerdFallbackSivalPtrRequestDump = reinterpret_cast<void*>(~0UL);
+static const uintptr_t kDebuggerdFallbackSivalUintptrRequestDump = ~0UL;
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 15ae406..ad92067 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -212,8 +212,13 @@
   bool intercepted =
       intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
   if (!intercepted) {
-    std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
-    crash->crash_tombstone_fd.reset(dup(output_fd.get()));
+    if (crash->crash_type == kDebuggerdNativeBacktrace) {
+      // Don't generate tombstones for native backtrace requests.
+      output_fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
+    } else {
+      std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
+      crash->crash_tombstone_fd.reset(dup(output_fd.get()));
+    }
   }
 
   TombstonedCrashPacket response = {
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 1152007..e433787 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -39,27 +39,27 @@
     strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size) {
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out) {
     const size_t page_mask = src.page_size - 1;
 
     int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
-    int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
-    int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
-    int64_t second_actual = (second_size + page_mask) & (~page_mask);
+    int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
+    int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
+    int64_t second_actual = (second.size() + page_mask) & (~page_mask);
 
-    *bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    int64_t bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    out->resize(bootimg_size);
 
-    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
-    if (hdr == nullptr) die("couldn't allocate boot image: %" PRId64 " bytes", *bootimg_size);
+    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(out->data());
 
     *hdr = src;
     memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
 
-    hdr->kernel_size =  kernel_size;
-    hdr->ramdisk_size = ramdisk_size;
-    hdr->second_size =  second_size;
+    hdr->kernel_size = kernel.size();
+    hdr->ramdisk_size = ramdisk.size();
+    hdr->second_size = second.size();
 
     hdr->kernel_addr += base;
     hdr->ramdisk_addr += base;
@@ -70,8 +70,9 @@
         hdr->header_size = sizeof(boot_img_hdr_v1);
     }
 
-    memcpy(hdr->magic + hdr->page_size, kernel, kernel_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk, ramdisk_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second, second_size);
+    memcpy(hdr->magic + hdr->page_size, kernel.data(), kernel.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),
+           second.size());
     return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index fe805b0..a4e8870 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -33,8 +33,9 @@
 #include <sys/types.h>
 
 #include <string>
+#include <vector>
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size);
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out);
 void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 57e25fc..2a68a2b 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -32,6 +32,7 @@
 #define FB_CMD_DELETE_PARTITION "delete-logical-partition"
 #define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
 #define FB_CMD_UPDATE_SUPER "update-super"
+#define FB_CMD_OEM "oem"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 9914227..b7cafac 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -40,6 +40,9 @@
 using ::android::hardware::boot::V1_0::BoolResult;
 using ::android::hardware::boot::V1_0::CommandResult;
 using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
+
 using namespace android::fs_mgr;
 
 struct VariableHandlers {
@@ -133,6 +136,24 @@
     return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
 }
 
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
+    if (!ret_val.isOk()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to do OEM command");
+    }
+    if (ret.status != Status::SUCCESS) {
+        return device->WriteStatus(FastbootResult::FAIL, ret.message);
+    }
+
+    return device->WriteStatus(FastbootResult::OKAY, ret.message);
+}
+
 bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 4778d23..9df43a9 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -45,3 +45,4 @@
 bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index ae2e7a6..6862741 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -48,6 +48,7 @@
               {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
               {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
               {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
+              {FB_CMD_OEM, OemCmdHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
@@ -120,10 +121,20 @@
         command[bytes_read] = '\0';
 
         LOG(INFO) << "Fastboot command: " << command;
-        auto args = android::base::Split(command, ":");
-        auto found_command = kCommandMap.find(args[0]);
+
+        std::vector<std::string> args;
+        std::string cmd_name;
+        if (android::base::StartsWith(command, "oem ")) {
+            args = {command};
+            cmd_name = "oem";
+        } else {
+            args = android::base::Split(command, ":");
+            cmd_name = args[0];
+        }
+
+        auto found_command = kCommandMap.find(cmd_name);
         if (found_command == kCommandMap.end()) {
-            WriteStatus(FastbootResult::FAIL, "Unrecognized command");
+            WriteStatus(FastbootResult::FAIL, "Unrecognized command " + args[0]);
             continue;
         }
         if (!found_command->second(this, args)) {
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 0ac57af..a43e7a6 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -103,9 +103,9 @@
     RUN_COMMAND(fb->Flash(partition));
 }
 
-void fb_flash(const std::string& partition, void* data, uint32_t sz) {
-    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
-    RUN_COMMAND(fb->Download(static_cast<char*>(data), sz));
+void fb_flash(const std::string& partition, const std::vector<char>& data) {
+    Status(StringPrintf("Sending '%s' (%zu KB)", partition.c_str(), data.size() / 1024));
+    RUN_COMMAND(fb->Download(data));
 
     Status("Writing '" + partition + "'");
     RUN_COMMAND(fb->Flash(partition));
@@ -136,69 +136,6 @@
     RUN_COMMAND(fb->RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size));
 }
 
-static int match(const char* str, const char** value, unsigned count) {
-    unsigned n;
-
-    for (n = 0; n < count; n++) {
-        const char *val = value[n];
-        int len = strlen(val);
-        int match;
-
-        if ((len > 1) && (val[len-1] == '*')) {
-            len--;
-            match = !strncmp(val, str, len);
-        } else {
-            match = !strcmp(val, str);
-        }
-
-        if (match) return 1;
-    }
-
-    return 0;
-}
-
-void fb_require(const std::string& product, const std::string& var, bool invert, size_t count,
-                const char** values) {
-    Status("Checking '" + var + "'");
-
-    double start = now();
-
-    std::string var_value;
-    auto status = fb->GetVar(var, &var_value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        die("requirements not met!");
-    }
-
-    if (!product.empty()) {
-        if (product != cur_product) {
-            double split = now();
-            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
-                    product.c_str(), (split - start));
-            return;
-        }
-    }
-
-    int yes = match(var_value.c_str(), values, count);
-    if (invert) yes = !yes;
-
-    if (yes) {
-        double split = now();
-        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
-        return;
-    }
-
-    fprintf(stderr, "FAILED\n\n");
-    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
-    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", values[0]);
-    for (size_t n = 1; n < count; n++) {
-        fprintf(stderr, " or '%s'", values[n]);
-    }
-    fprintf(stderr, ".\n\n");
-    die("requirements not met!");
-}
-
 void fb_display(const std::string& label, const std::string& var) {
     std::string value;
     auto status = fb->GetVar(var, &value);
@@ -210,18 +147,6 @@
     fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
 }
 
-void fb_query_save(const std::string& var, char* dest, uint32_t dest_size) {
-    std::string value;
-    auto status = fb->GetVar(var, &value);
-
-    if (status) {
-        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
-        return;
-    }
-
-    strncpy(dest, value.c_str(), dest_size);
-}
-
 void fb_reboot() {
     fprintf(stderr, "Rebooting");
     fb->Reboot();
@@ -233,9 +158,9 @@
     RUN_COMMAND(fb->RawCommand(cmd));
 }
 
-void fb_download(const std::string& name, void* data, uint32_t size) {
+void fb_download(const std::string& name, const std::vector<char>& data) {
     Status("Downloading '" + name + "'");
-    RUN_COMMAND(fb->Download(static_cast<char*>(data), size));
+    RUN_COMMAND(fb->Download(data));
 }
 
 void fb_download_fd(const std::string& name, int fd, uint32_t sz) {
diff --git a/fastboot/engine.h b/fastboot/engine.h
index d78cb13..b681f5a 100644
--- a/fastboot/engine.h
+++ b/fastboot/engine.h
@@ -48,18 +48,15 @@
 void fb_reinit(Transport* transport);
 
 bool fb_getvar(const std::string& key, std::string* value);
-void fb_flash(const std::string& partition, void* data, uint32_t sz);
+void fb_flash(const std::string& partition, const std::vector<char>& data);
 void fb_flash_fd(const std::string& partition, int fd, uint32_t sz);
 void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
                      size_t current, size_t total);
 void fb_erase(const std::string& partition);
-void fb_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
-                const char** values);
 void fb_display(const std::string& label, const std::string& var);
-void fb_query_save(const std::string& var, char* dest, uint32_t dest_size);
 void fb_reboot();
 void fb_command(const std::string& cmd, const std::string& msg);
-void fb_download(const std::string& name, void* data, uint32_t size);
+void fb_download(const std::string& name, const std::vector<char>& data);
 void fb_download_fd(const std::string& name, int fd, uint32_t sz);
 void fb_upload(const std::string& outfile);
 void fb_notice(const std::string& notice);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 817afd0..5173bab 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,7 @@
 
 #include <chrono>
 #include <functional>
+#include <regex>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -69,14 +70,15 @@
 #include "udp.h"
 #include "usb.h"
 
+using android::base::ReadFully;
+using android::base::Split;
+using android::base::Trim;
 using android::base::unique_fd;
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-char cur_product[FB_RESPONSE_SZ + 1];
-
 static const char* serial = nullptr;
 
 static bool g_long_listing = false;
@@ -148,6 +150,11 @@
     { nullptr,    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
     { "userdata", "userdata.img",     "userdata.sig", "userdata", true,  ImageType::Extra },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  ImageType::BootCritical },
+    { "vbmeta_mainline",
+                  "vbmeta_mainline.img",
+                                      "vbmeta_mainline.sig",
+                                                      "vbmeta_mainline",
+                                                                  true,  ImageType::BootCritical },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
@@ -174,38 +181,22 @@
 
 static int64_t get_file_size(int fd) {
     struct stat sb;
-    return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
+    if (fstat(fd, &sb) == -1) {
+        die("could not get file size");
+    }
+    return sb.st_size;
 }
 
-static void* load_fd(int fd, int64_t* sz) {
-    int errno_tmp;
-    char* data = nullptr;
+bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
+    out->clear();
 
-    *sz = get_file_size(fd);
-    if (*sz < 0) {
-        goto oops;
+    unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));
+    if (fd == -1) {
+        return false;
     }
 
-    data = (char*) malloc(*sz);
-    if (data == nullptr) goto oops;
-
-    if(read(fd, data, *sz) != *sz) goto oops;
-    close(fd);
-
-    return data;
-
-oops:
-    errno_tmp = errno;
-    close(fd);
-    if(data != 0) free(data);
-    errno = errno_tmp;
-    return 0;
-}
-
-static void* load_file(const std::string& path, int64_t* sz) {
-    int fd = open(path.c_str(), O_RDONLY | O_BINARY);
-    if (fd == -1) return nullptr;
-    return load_fd(fd, sz);
+    out->resize(get_file_size(fd));
+    return ReadFully(fd, out->data(), out->size());
 }
 
 static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
@@ -413,70 +404,71 @@
     return 0;
 }
 
-static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
-                                 const std::string& second_stage, int64_t* sz) {
-    int64_t ksize;
-    void* kdata = load_file(kernel.c_str(), &ksize);
-    if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+static std::vector<char> LoadBootableImage(const std::string& kernel, const std::string& ramdisk,
+                                           const std::string& second_stage) {
+    std::vector<char> kernel_data;
+    if (!ReadFileToVector(kernel, &kernel_data)) {
+        die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+    }
 
     // Is this actually a boot image?
-    if (ksize < static_cast<int64_t>(sizeof(boot_img_hdr_v1))) {
+    if (kernel_data.size() < sizeof(boot_img_hdr_v1)) {
         die("cannot load '%s': too short", kernel.c_str());
     }
-    if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+    if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
         if (!g_cmdline.empty()) {
-            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), g_cmdline);
+            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kernel_data.data()), g_cmdline);
         }
 
         if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
 
-        *sz = ksize;
-        return kdata;
+        return kernel_data;
     }
 
-    void* rdata = nullptr;
-    int64_t rsize = 0;
+    std::vector<char> ramdisk_data;
     if (!ramdisk.empty()) {
-        rdata = load_file(ramdisk.c_str(), &rsize);
-        if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        if (!ReadFileToVector(ramdisk, &ramdisk_data)) {
+            die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        }
     }
 
-    void* sdata = nullptr;
-    int64_t ssize = 0;
+    std::vector<char> second_stage_data;
     if (!second_stage.empty()) {
-        sdata = load_file(second_stage.c_str(), &ssize);
-        if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        if (!ReadFileToVector(second_stage, &second_stage_data)) {
+            die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        }
     }
-
     fprintf(stderr,"creating boot image...\n");
-    boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, rdata, rsize, sdata, ssize,
-                                       g_base_addr, g_boot_img_hdr, sz);
-    if (bdata == nullptr) die("failed to create boot.img");
 
-    if (!g_cmdline.empty()) bootimg_set_cmdline(bdata, g_cmdline);
-    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", *sz);
+    std::vector<char> out;
+    boot_img_hdr_v1* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+                                                 g_base_addr, g_boot_img_hdr, &out);
 
-    return bdata;
+    if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
+    fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
+
+    return out;
 }
 
-static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
-    ZipString zip_entry_name(entry_name);
+static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
+                          std::vector<char>* out) {
+    ZipString zip_entry_name(entry_name.c_str());
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
-        fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        return nullptr;
+        fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
+        return false;
     }
 
-    *sz = zip_entry.uncompressed_length;
+    out->resize(zip_entry.uncompressed_length);
 
-    fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
-    uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
+    fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
+            out->size() / 1024 / 1024);
 
-    int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
-    if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
+    int error = ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()),
+                                out->size());
+    if (error != 0) die("failed to extract '%s': %s", entry_name.c_str(), ErrorCodeString(error));
 
-    return data;
+    return true;
 }
 
 #if defined(_WIN32)
@@ -597,109 +589,145 @@
     return fd.release();
 }
 
-static char* strip(char* s) {
-    while (*s && isspace(*s)) s++;
+static void CheckRequirement(const std::string& cur_product, const std::string& var,
+                             const std::string& product, bool invert,
+                             const std::vector<std::string>& options) {
+    Status("Checking '" + var + "'");
 
-    int n = strlen(s);
-    while (n-- > 0) {
-        if (!isspace(s[n])) break;
-        s[n] = 0;
+    double start = now();
+
+    if (!product.empty()) {
+        if (product != cur_product) {
+            double split = now();
+            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
+                    cur_product.c_str(), product.c_str(), (split - start));
+            return;
+        }
     }
-    return s;
+
+    std::string var_value;
+    if (!fb_getvar(var, &var_value)) {
+        fprintf(stderr, "FAILED\n\n");
+        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(), fb_get_error().c_str());
+        die("requirements not met!");
+    }
+
+    bool match = false;
+    for (const auto& option : options) {
+        if (option == var_value || (option.back() == '*' &&
+                                    !var_value.compare(0, option.length() - 1, option, 0,
+                                                       option.length() - 1))) {
+            match = true;
+            break;
+        }
+    }
+
+    if (invert) {
+        match = !match;
+    }
+
+    if (match) {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
+        return;
+    }
+
+    fprintf(stderr, "FAILED\n\n");
+    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
+    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", options[0].c_str());
+    for (auto it = std::next(options.begin()); it != options.end(); ++it) {
+        fprintf(stderr, " or '%s'", it->c_str());
+    }
+    fprintf(stderr, ".\n\n");
+    die("requirements not met!");
 }
 
-#define MAX_OPTIONS 32
-static void check_requirement(char* line) {
-    char *val[MAX_OPTIONS];
-    unsigned count;
-    char *x;
-    int invert = 0;
-
+bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                          bool* invert, std::vector<std::string>* options) {
     // "require product=alpha|beta|gamma"
     // "require version-bootloader=1234"
     // "require-for-product:gamma version-bootloader=istanbul|constantinople"
     // "require partition-exists=vendor"
+    *product = "";
+    *invert = false;
 
-    char* name = line;
-    const char* product = "";
-    if (!strncmp(name, "reject ", 7)) {
-        name += 7;
-        invert = 1;
-    } else if (!strncmp(name, "require ", 8)) {
-        name += 8;
-        invert = 0;
-    } else if (!strncmp(name, "require-for-product:", 20)) {
-        // Get the product and point name past it
-        product = name + 20;
-        name = strchr(name, ' ');
-        if (!name) die("android-info.txt syntax error: %s", line);
-        *name = 0;
-        name += 1;
-        invert = 0;
+    auto require_reject_regex = std::regex{"(require\\s+|reject\\s+)?\\s*(\\S+)\\s*=\\s*(.*)"};
+    auto require_product_regex =
+            std::regex{"require-for-product:\\s*(\\S+)\\s+(\\S+)\\s*=\\s*(.*)"};
+    std::smatch match_results;
+
+    if (std::regex_match(line, match_results, require_reject_regex)) {
+        *invert = Trim(match_results[1]) == "reject";
+    } else if (std::regex_match(line, match_results, require_product_regex)) {
+        *product = match_results[1];
+    } else {
+        return false;
     }
 
-    x = strchr(name, '=');
-    if (x == 0) return;
-    *x = 0;
-    val[0] = x + 1;
-
-    name = strip(name);
-
-    // "require partition-exists=x" is a special case, added because of the trouble we had when
-    // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
-    // missing out new partitions. A device with new partitions can use "partition-exists" to
-    // override the fields `optional_if_no_image` in the `images` array.
-    if (!strcmp(name, "partition-exists")) {
-        const char* partition_name = val[0];
-        std::string has_slot;
-        if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
-            (has_slot != "yes" && has_slot != "no")) {
-            die("device doesn't have required partition %s!", partition_name);
-        }
-        bool known_partition = false;
-        for (size_t i = 0; i < arraysize(images); ++i) {
-            if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
-                images[i].optional_if_no_image = false;
-                known_partition = true;
-            }
-        }
-        if (!known_partition) {
-            die("device requires partition %s which is not known to this version of fastboot",
-                partition_name);
-        }
-        return;
-    }
-
-    for(count = 1; count < MAX_OPTIONS; count++) {
-        x = strchr(val[count - 1],'|');
-        if (x == 0) break;
-        *x = 0;
-        val[count] = x + 1;
-    }
-
+    *name = match_results[2];
     // Work around an unfortunate name mismatch.
-    const char* var = name;
-    if (!strcmp(name, "board")) var = "product";
-
-    const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
-    if (out == nullptr) die("out of memory");
-
-    for (size_t i = 0; i < count; ++i) {
-        out[i] = xstrdup(strip(val[i]));
+    if (*name == "board") {
+        *name = "product";
     }
 
-    fb_require(product, var, invert, count, out);
+    auto raw_options = Split(match_results[3], "|");
+    for (const auto& option : raw_options) {
+        auto trimmed_option = Trim(option);
+        options->emplace_back(trimmed_option);
+    }
+
+    return true;
 }
 
-static void check_requirements(char* data, int64_t sz) {
-    char* s = data;
-    while (sz-- > 0) {
-        if (*s == '\n') {
-            *s++ = 0;
-            check_requirement(data);
-            data = s;
+// "require partition-exists=x" is a special case, added because of the trouble we had when
+// Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
+// missing out new partitions. A device with new partitions can use "partition-exists" to
+// override the fields `optional_if_no_image` in the `images` array.
+static void HandlePartitionExists(const std::vector<std::string>& options) {
+    const std::string& partition_name = options[0];
+    std::string has_slot;
+    if (!fb_getvar("has-slot:" + partition_name, &has_slot) ||
+        (has_slot != "yes" && has_slot != "no")) {
+        die("device doesn't have required partition %s!", partition_name.c_str());
+    }
+    bool known_partition = false;
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        if (images[i].nickname && images[i].nickname == partition_name) {
+            images[i].optional_if_no_image = false;
+            known_partition = true;
+        }
+    }
+    if (!known_partition) {
+        die("device requires partition %s which is not known to this version of fastboot",
+            partition_name.c_str());
+    }
+}
+
+static void CheckRequirements(const std::string& data) {
+    std::string cur_product;
+    if (!fb_getvar("product", &cur_product)) {
+        fprintf(stderr, "getvar:product FAILED (%s)\n", fb_get_error().c_str());
+    }
+
+    auto lines = Split(data, "\n");
+    for (const auto& line : lines) {
+        if (line.empty()) {
+            continue;
+        }
+
+        std::string name;
+        std::string product;
+        bool invert;
+        std::vector<std::string> options;
+
+        if (!ParseRequirementLine(line, &name, &product, &invert, &options)) {
+            fprintf(stderr, "android-info.txt syntax error: %s\n", line.c_str());
+            continue;
+        }
+        if (name == "partition-exists") {
+            HandlePartitionExists(options);
         } else {
-            s++;
+            CheckRequirement(cur_product, name, product, invert, options);
         }
     }
 }
@@ -1092,7 +1120,7 @@
 
 class ImageSource {
   public:
-    virtual void* ReadFile(const std::string& name, int64_t* size) const = 0;
+    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
     virtual int OpenFile(const std::string& name) const = 0;
 };
 
@@ -1161,12 +1189,11 @@
 }
 
 void FlashAllTool::CheckRequirements() {
-    int64_t sz;
-    void* data = source_.ReadFile("android-info.txt", &sz);
-    if (data == nullptr) {
+    std::vector<char> contents;
+    if (!source_.ReadFile("android-info.txt", &contents)) {
         die("could not read android-info.txt");
     }
-    check_requirements(reinterpret_cast<char*>(data), sz);
+    ::CheckRequirements({contents.data(), contents.size()});
 }
 
 void FlashAllTool::DetermineSecondarySlot() {
@@ -1219,10 +1246,9 @@
 
 void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
     auto flash = [&, this](const std::string& partition_name) {
-        int64_t sz;
-        void* data = source_.ReadFile(image.sig_name, &sz);
-        if (data) {
-            fb_download("signature", data, sz);
+        std::vector<char> signature_data;
+        if (source_.ReadFile(image.sig_name, &signature_data)) {
+            fb_download("signature", signature_data);
             fb_command("signature", "installing signature");
         }
 
@@ -1258,15 +1284,15 @@
 class ZipImageSource final : public ImageSource {
   public:
     explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
-    void* ReadFile(const std::string& name, int64_t* size) const override;
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
     int OpenFile(const std::string& name) const override;
 
   private:
     ZipArchiveHandle zip_;
 };
 
-void* ZipImageSource::ReadFile(const std::string& name, int64_t* size) const {
-    return unzip_to_memory(zip_, name.c_str(), size);
+bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+    return UnzipToMemory(zip_, name, out);
 }
 
 int ZipImageSource::OpenFile(const std::string& name) const {
@@ -1276,8 +1302,6 @@
 static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     dump_info();
 
-    fb_query_save("product", cur_product, sizeof(cur_product));
-
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
@@ -1292,16 +1316,16 @@
 
 class LocalImageSource final : public ImageSource {
   public:
-    void* ReadFile(const std::string& name, int64_t* size) const override;
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
     int OpenFile(const std::string& name) const override;
 };
 
-void* LocalImageSource::ReadFile(const std::string& name, int64_t* size) const {
+bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
     auto path = find_item_given_name(name);
     if (path.empty()) {
-        return nullptr;
+        return false;
     }
-    return load_file(path.c_str(), size);
+    return ReadFileToVector(path, out);
 }
 
 int LocalImageSource::OpenFile(const std::string& name) const {
@@ -1313,8 +1337,6 @@
     std::string fname;
     dump_info();
 
-    fb_query_save("product", cur_product, sizeof(cur_product));
-
     FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
     tool.Flash();
 }
@@ -1468,8 +1490,6 @@
     bool wants_set_active = false;
     bool skip_secondary = false;
     bool set_fbe_marker = false;
-    void *data;
-    int64_t sz;
     int longindex;
     std::string slot_override;
     std::string next_active;
@@ -1673,10 +1693,12 @@
             do_for_partitions(partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
-            data = load_file(filename.c_str(), &sz);
-            if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
-            if (sz != 256) die("signature must be 256 bytes (got %" PRId64 ")", sz);
-            fb_download("signature", data, sz);
+            std::vector<char> data;
+            if (!ReadFileToVector(filename, &data)) {
+                die("could not load '%s': %s", filename.c_str(), strerror(errno));
+            }
+            if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
+            fb_download("signature", data);
             fb_command("signature", "installing signature");
         } else if (command == "reboot") {
             wants_reboot = true;
@@ -1713,8 +1735,8 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            fb_download("boot.img", data, sz);
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            fb_download("boot.img", data);
             fb_command("boot", "booting");
         } else if (command == "flash") {
             std::string pname = next_arg(&args);
@@ -1739,9 +1761,9 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            auto flashraw = [&](const std::string& partition) {
-                fb_flash(partition, data, sz);
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            auto flashraw = [&data](const std::string& partition) {
+                fb_flash(partition, data);
             };
             do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 4a14131..72ba619 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -147,28 +147,6 @@
     return SUCCESS;
 }
 
-RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
-                                bool* reqmet, bool invert) {
-    *reqmet = invert;
-    RetCode ret;
-    std::string response;
-    if ((ret = GetVar(var, &response))) {
-        return ret;
-    }
-
-    // Now check if we have a match
-    for (const auto s : allowed) {
-        // If it ends in *, and starting substring match
-        if (response == s || (s.length() && s.back() == '*' &&
-                              !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
-            *reqmet = !invert;
-            break;
-        }
-    }
-
-    return SUCCESS;
-}
-
 RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
@@ -194,24 +172,19 @@
 
 RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
                                  std::vector<std::string>* info) {
-    return Download(buf.data(), buf.size(), response, info);
-}
-
-RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
-                                 std::vector<std::string>* info) {
     RetCode ret;
     error_ = "";
-    if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+    if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
         error_ = "Buffer is too large or 0 bytes";
         return BAD_ARG;
     }
 
-    if ((ret = DownloadCommand(size, response, info))) {
+    if ((ret = DownloadCommand(buf.size(), response, info))) {
         return ret;
     }
 
     // Write the buffer
-    if ((ret = SendBuffer(buf, size))) {
+    if ((ret = SendBuffer(buf))) {
         return ret;
     }
 
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 4d85ba0..2d45085 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -74,9 +74,6 @@
                      std::vector<std::string>* info = nullptr);
     RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
-    // This will be removed after fastboot is modified to use a vector
-    RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
-                     std::vector<std::string>* info = nullptr);
     RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Erase(const std::string& part, std::string* response = nullptr,
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 43201fa..e0bbd56 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -59,3 +59,145 @@
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.128.3"), "bad OS version");
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.2.128"), "bad OS version");
 }
+
+extern bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                                 bool* invert, std::vector<std::string>* options);
+
+static void ParseRequirementLineTest(const std::string& line, const std::string& expected_name,
+                                     const std::string& expected_product, bool expected_invert,
+                                     const std::vector<std::string>& expected_options) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_TRUE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+
+    EXPECT_EQ(expected_name, name) << line;
+    EXPECT_EQ(expected_product, product) << line;
+    EXPECT_EQ(expected_invert, invert) << line;
+    EXPECT_EQ(expected_options, options) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineSuccesses) {
+    // Examples provided in the code + slight variations.
+    ParseRequirementLineTest("require product=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require version-bootloader=1234", "version-bootloader", "", false,
+                             {"1234"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul|constantinople",
+                             "version-bootloader", "gamma", false, {"istanbul", "constantinople"});
+    ParseRequirementLineTest("require partition-exists=vendor", "partition-exists", "", false,
+                             {"vendor"});
+    ParseRequirementLineTest("reject product=alpha", "product", "", true, {"alpha"});
+    ParseRequirementLineTest("reject product=alpha|beta|gamma", "product", "", true,
+                             {"alpha", "beta", "gamma"});
+
+    // Without any prefix, assume 'require'
+    ParseRequirementLineTest("product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    // Including if the variable name is otherwise a prefix keyword
+    ParseRequirementLineTest("require = alpha", "require", "", false, {"alpha"});
+    ParseRequirementLineTest("reject = alpha", "reject", "", false, {"alpha"});
+    ParseRequirementLineTest("require-for-product:gamma = alpha", "require-for-product:gamma", "",
+                             false, {"alpha"});
+
+    // Extra spaces are allowed.
+    ParseRequirementLineTest("require    product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product    =alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product   =   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("product  =  alpha  |  beta  |  gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require-for-product:  gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+
+    // Extraneous ending | is okay, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha|", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta|gamma|", "product", "", false,
+                             {"alpha", "beta", "gamma", ""});
+
+    // Accept empty options, double ||, etc, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha||beta|   |gamma", "product", "", false,
+                             {"alpha", "", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||beta|gamma", "product", "", false,
+                             {"alpha", "", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|   |gamma", "product", "", false,
+                             {"alpha", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha|| ", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha| ", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta| ", "product", "", false,
+                             {"alpha", "beta", ""});
+
+    // No option string is also treating as accepting an empty string.
+    ParseRequirementLineTest("require =", "require", "", false, {""});
+    ParseRequirementLineTest("require = |", "require", "", false, {"", ""});
+    ParseRequirementLineTest("reject =", "reject", "", false, {""});
+    ParseRequirementLineTest("reject = |", "reject", "", false, {"", ""});
+    ParseRequirementLineTest("require-for-product: =", "require-for-product:", "", false, {""});
+    ParseRequirementLineTest("require-for-product: = | ", "require-for-product:", "", false,
+                             {"", ""});
+    ParseRequirementLineTest("require product=", "product", "", false, {""});
+    ParseRequirementLineTest("require product = ", "product", "", false, {""});
+    ParseRequirementLineTest("require product = | ", "product", "", false, {"", ""});
+    ParseRequirementLineTest("reject product=", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = ", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = | ", "product", "", true, {"", ""});
+    ParseRequirementLineTest("require-for-product:gamma product=", "product", "gamma", false, {""});
+    ParseRequirementLineTest("require-for-product:gamma product = ", "product", "gamma", false,
+                             {""});
+    ParseRequirementLineTest("require-for-product:gamma product = |", "product", "gamma", false,
+                             {"", ""});
+
+    // Check for board -> product substitution.
+    ParseRequirementLineTest("require board=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("board=alpha", "product", "", false, {"alpha"});
+}
+
+static void ParseRequirementLineTestMalformed(const std::string& line) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_FALSE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineMalformed) {
+    ParseRequirementLineTestMalformed("nothing");
+    ParseRequirementLineTestMalformed("");
+    ParseRequirementLineTestMalformed("=");
+    ParseRequirementLineTestMalformed("|");
+
+    ParseRequirementLineTestMalformed("require");
+    ParseRequirementLineTestMalformed("require ");
+    ParseRequirementLineTestMalformed("reject");
+    ParseRequirementLineTestMalformed("reject ");
+    ParseRequirementLineTestMalformed("require-for-product:");
+    ParseRequirementLineTestMalformed("require-for-product: ");
+
+    ParseRequirementLineTestMalformed("require product");
+    ParseRequirementLineTestMalformed("reject product");
+
+    ParseRequirementLineTestMalformed("require-for-product:gamma");
+    ParseRequirementLineTestMalformed("require-for-product:gamma product");
+
+    // No spaces allowed before between require-for-product and :.
+    ParseRequirementLineTestMalformed("require-for-product :");
+}
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 125182d..7d15047 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -74,9 +74,3 @@
     static constexpr char kStatusFormat[] = "%-50s ";
     fprintf(stderr, kStatusFormat, message.c_str());
 }
-
-char* xstrdup(const char* s) {
-    char* result = strdup(s);
-    if (!result) die("out of memory");
-    return result;
-}
diff --git a/fastboot/util.h b/fastboot/util.h
index 20be461..533d2c7 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -9,7 +9,6 @@
 
 /* util stuff */
 double now();
-char* xstrdup(const char*);
 void set_verbose();
 
 void Status(const std::string& message);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index f56043c..d29ccf4 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -569,8 +569,7 @@
  *   -1 on failure with errno set to match the 1st mount failure.
  *   0 on success.
  */
-static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
-{
+static int mount_with_alternatives(fstab* fstab, int start_idx, int* end_idx, int* attempted_idx) {
     int i;
     int mount_errno = 0;
     int mounted = 0;
@@ -805,6 +804,23 @@
     return true;
 }
 
+static bool call_vdc_ret(const std::vector<std::string>& args, int* ret) {
+    std::vector<char const*> argv;
+    argv.emplace_back("/system/bin/vdc");
+    for (auto& arg : args) {
+        argv.emplace_back(arg.c_str());
+    }
+    LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
+    int err = android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), ret, false, true);
+    if (err != 0) {
+        LOG(ERROR) << "vdc call failed with error code: " << err;
+        return false;
+    }
+    LOG(DEBUG) << "vdc finished successfully";
+    *ret = WEXITSTATUS(*ret);
+    return true;
+}
+
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
     // Logical partitions are specified with a named partition rather than a
     // block device, so if the block device is a path, then it has already
@@ -823,19 +839,37 @@
     return true;
 }
 
+bool fs_mgr_update_checkpoint_partition(struct fstab_rec* rec) {
+    if (fs_mgr_is_checkpoint(rec)) {
+        if (!strcmp(rec->fs_type, "f2fs")) {
+            std::string opts(rec->fs_options);
+
+            opts += ",checkpoint=disable";
+            free(rec->fs_options);
+            rec->fs_options = strdup(opts.c_str());
+        } else {
+            LERROR << rec->fs_type << " does not implement checkpoints.";
+        }
+    } else if (rec->fs_mgr_flags & MF_CHECKPOINT_BLK) {
+        LERROR << "Block based checkpoint not implemented.";
+        return false;
+    }
+    return true;
+}
+
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
  * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
  */
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
-{
+int fs_mgr_mount_all(fstab* fstab, int mount_mode) {
     int i = 0;
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     int error_count = 0;
     int mret = -1;
     int mount_errno = 0;
     int attempted_idx = -1;
+    int need_checkpoint = -1;
     FsManagerAvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -882,6 +916,18 @@
             }
         }
 
+        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+            if (need_checkpoint == -1 &&
+                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+                need_checkpoint = 0;
+            }
+            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up checkpoint partition, skipping!";
+                continue;
+            }
+        }
+
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
             !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
             LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
@@ -1045,7 +1091,7 @@
     }
 
 #if ALLOW_ADBD_DISABLE_VERITY == 1  // "userdebug" build
-    fs_mgr_overlayfs_mount_all();
+    fs_mgr_overlayfs_mount_all(fstab);
 #endif
 
     if (error_count) {
@@ -1081,9 +1127,8 @@
  * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
  * in turn, and stop on 1st success, or no more match.
  */
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
-                    char *tmp_mount_point)
-{
+static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
+                                  char* tmp_mount_point, int need_checkpoint) {
     int i = 0;
     int mount_errors = 0;
     int first_mount_errno = 0;
@@ -1116,6 +1161,18 @@
             }
         }
 
+        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+            if (need_checkpoint == -1 &&
+                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+                need_checkpoint = 0;
+            }
+            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up checkpoint partition, skipping!";
+                continue;
+            }
+        }
+
         /* First check the filesystem if requested */
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
@@ -1185,6 +1242,15 @@
     return FS_MGR_DOMNT_FAILED;
 }
 
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+}
+
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+                    bool needs_cp) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_cp);
+}
+
 /*
  * mount a tmpfs filesystem at the given point.
  * return 0 on success, non-zero on failure.
@@ -1207,8 +1273,7 @@
 /* This must be called after mount_all, because the mkswap command needs to be
  * available.
  */
-int fs_mgr_swapon_all(struct fstab *fstab)
-{
+int fs_mgr_swapon_all(fstab* fstab) {
     int i = 0;
     int flags = 0;
     int err = 0;
@@ -1298,7 +1363,7 @@
     return ret;
 }
 
-struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
+struct fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab) {
     int i;
 
     if (!fstab) {
@@ -1322,7 +1387,7 @@
  *
  * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
  */
-void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
+void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
     struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
     if (key_loc) {
         if (rec) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f87a3b1..250793a 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -80,37 +80,39 @@
 };
 
 static struct flag_list fs_mgr_flags[] = {
-    {"wait", MF_WAIT},
-    {"check", MF_CHECK},
-    {"encryptable=", MF_CRYPT},
-    {"forceencrypt=", MF_FORCECRYPT},
-    {"fileencryption=", MF_FILEENCRYPTION},
-    {"forcefdeorfbe=", MF_FORCEFDEORFBE},
-    {"keydirectory=", MF_KEYDIRECTORY},
-    {"nonremovable", MF_NONREMOVABLE},
-    {"voldmanaged=", MF_VOLDMANAGED},
-    {"length=", MF_LENGTH},
-    {"recoveryonly", MF_RECOVERYONLY},
-    {"swapprio=", MF_SWAPPRIO},
-    {"zramsize=", MF_ZRAMSIZE},
-    {"max_comp_streams=", MF_MAX_COMP_STREAMS},
-    {"verifyatboot", MF_VERIFYATBOOT},
-    {"verify", MF_VERIFY},
-    {"avb", MF_AVB},
-    {"noemulatedsd", MF_NOEMULATEDSD},
-    {"notrim", MF_NOTRIM},
-    {"formattable", MF_FORMATTABLE},
-    {"slotselect", MF_SLOTSELECT},
-    {"nofail", MF_NOFAIL},
-    {"latemount", MF_LATEMOUNT},
-    {"reservedsize=", MF_RESERVEDSIZE},
-    {"quota", MF_QUOTA},
-    {"eraseblk=", MF_ERASEBLKSIZE},
-    {"logicalblk=", MF_LOGICALBLKSIZE},
-    {"sysfs_path=", MF_SYSFS},
-    {"defaults", 0},
-    {"logical", MF_LOGICAL},
-    {0, 0},
+        {"wait", MF_WAIT},
+        {"check", MF_CHECK},
+        {"encryptable=", MF_CRYPT},
+        {"forceencrypt=", MF_FORCECRYPT},
+        {"fileencryption=", MF_FILEENCRYPTION},
+        {"forcefdeorfbe=", MF_FORCEFDEORFBE},
+        {"keydirectory=", MF_KEYDIRECTORY},
+        {"nonremovable", MF_NONREMOVABLE},
+        {"voldmanaged=", MF_VOLDMANAGED},
+        {"length=", MF_LENGTH},
+        {"recoveryonly", MF_RECOVERYONLY},
+        {"swapprio=", MF_SWAPPRIO},
+        {"zramsize=", MF_ZRAMSIZE},
+        {"max_comp_streams=", MF_MAX_COMP_STREAMS},
+        {"verifyatboot", MF_VERIFYATBOOT},
+        {"verify", MF_VERIFY},
+        {"avb", MF_AVB},
+        {"noemulatedsd", MF_NOEMULATEDSD},
+        {"notrim", MF_NOTRIM},
+        {"formattable", MF_FORMATTABLE},
+        {"slotselect", MF_SLOTSELECT},
+        {"nofail", MF_NOFAIL},
+        {"latemount", MF_LATEMOUNT},
+        {"reservedsize=", MF_RESERVEDSIZE},
+        {"quota", MF_QUOTA},
+        {"eraseblk=", MF_ERASEBLKSIZE},
+        {"logicalblk=", MF_LOGICALBLKSIZE},
+        {"sysfs_path=", MF_SYSFS},
+        {"defaults", 0},
+        {"logical", MF_LOGICAL},
+        {"checkpoint=block", MF_CHECKPOINT_BLK},
+        {"checkpoint=fs", MF_CHECKPOINT_FS},
+        {0, 0},
 };
 
 #define EM_AES_256_XTS  1
@@ -1004,3 +1006,15 @@
 int fs_mgr_is_logical(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_LOGICAL;
 }
+
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & (MF_CHECKPOINT_FS | MF_CHECKPOINT_BLK);
+}
+
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_CHECKPOINT_FS;
+}
+
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 720dcfd..c0bef2c 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -49,7 +49,7 @@
 
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab*) {
     return false;
 }
 
@@ -109,9 +109,9 @@
     struct statvfs vst;
     if (statvfs(mount_point, &vst)) return true;
 
-    static constexpr int percent = 1;  // 1%
+    static constexpr int kPercentThreshold = 1;  // 1%
 
-    return (vst.f_bfree >= (vst.f_blocks * percent / 100));
+    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
 bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
@@ -123,22 +123,24 @@
            !fs_mgr_filesystem_has_space(fsrec->mount_point);
 }
 
-constexpr char upper_name[] = "upper";
-constexpr char work_name[] = "work";
+const auto kUpperName = "upper"s;
+const auto kWorkName = "work"s;
+const auto kOverlayTopDir = "/overlay"s;
 
 std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
     if (!fs_mgr_is_dir(mount_point)) return "";
-    auto dir = kOverlayMountPoint + "/overlay/" + android::base::Basename(mount_point) + "/";
-    auto upper = dir + upper_name;
+    auto dir =
+            kOverlayMountPoint + kOverlayTopDir + "/" + android::base::Basename(mount_point) + "/";
+    auto upper = dir + kUpperName;
     if (!fs_mgr_is_dir(upper)) return "";
-    auto work = dir + work_name;
+    auto work = dir + kWorkName;
     if (!fs_mgr_is_dir(work)) return "";
     if (!fs_mgr_dir_is_writable(work)) return "";
     return dir;
 }
 
-constexpr char lowerdir_option[] = "lowerdir=";
-constexpr char upperdir_option[] = "upperdir=";
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
 
 // default options for mount_point, returns empty string for none available.
 std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
@@ -147,8 +149,8 @@
 
     auto context = fs_mgr_get_context(mount_point);
     if (!context.empty()) context = ",rootcontext="s + context;
-    return "override_creds=off,"s + lowerdir_option + mount_point + "," + upperdir_option +
-           candidate + upper_name + ",workdir=" + candidate + work_name + context;
+    return "override_creds=off,"s + kLowerdirOption + mount_point + "," + kUpperdirOption +
+           candidate + kUpperName + ",workdir=" + candidate + kWorkName + context;
 }
 
 bool fs_mgr_system_root_image(const fstab* fstab) {
@@ -192,6 +194,31 @@
     return overlayfs_in_kernel;
 }
 
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
+                                                               fs_mgr_free_fstab);
+    if (!fstab) return false;
+    const auto lowerdir = kLowerdirOption + mount_point;
+    for (auto i = 0; i < fstab->num_entries; ++i) {
+        const auto fsrec = &fstab->recs[i];
+        const auto fs_type = fsrec->fs_type;
+        if (!fs_type) continue;
+        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+        auto fsrec_mount_point = fsrec->mount_point;
+        if (!fsrec_mount_point) continue;
+        if (mount_point != fsrec_mount_point) continue;
+        const auto fs_options = fsrec->fs_options;
+        if (!fs_options) continue;
+        const auto options = android::base::Split(fs_options, ",");
+        for (const auto& opt : options) {
+            if (opt == lowerdir) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
     if (!fsrec) return false;
 
@@ -265,16 +292,16 @@
     return ret;
 }
 
-constexpr char overlayfs_file_context[] = "u:object_r:overlayfs_file:s0";
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
 
 bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
                                 bool* change) {
     auto ret = true;
-    auto fsrec_mount_point = overlay + android::base::Basename(mount_point) + "/";
+    auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
 
-    if (setfscreatecon(overlayfs_file_context)) {
+    if (setfscreatecon(kOverlayfsFileContext)) {
         ret = false;
-        PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
+        PERROR << "overlayfs setfscreatecon " << kOverlayfsFileContext;
     }
     auto save_errno = errno;
     if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
@@ -287,11 +314,11 @@
     }
 
     save_errno = errno;
-    if (!mkdir((fsrec_mount_point + work_name).c_str(), 0755)) {
+    if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << fsrec_mount_point << work_name;
+        PERROR << "overlayfs mkdir " << fsrec_mount_point << kWorkName;
     } else {
         errno = save_errno;
     }
@@ -302,7 +329,7 @@
         ret = false;
         PERROR << "overlayfs setfscreatecon " << new_context;
     }
-    auto upper = fsrec_mount_point + upper_name;
+    auto upper = fsrec_mount_point + kUpperName;
     save_errno = errno;
     if (!mkdir(upper.c_str(), 0755)) {
         if (change) *change = true;
@@ -325,7 +352,7 @@
     auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
     const auto opt_list = android::base::Split(options, ",");
     for (const auto opt : opt_list) {
-        if (android::base::StartsWith(opt, upperdir_option)) {
+        if (android::base::StartsWith(opt, kUpperdirOption)) {
             report = report + "," + opt;
             break;
         }
@@ -343,31 +370,6 @@
     }
 }
 
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
-            fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab);
-    if (!fstab) return false;
-    const auto lowerdir = std::string(lowerdir_option) + mount_point;
-    for (auto i = 0; i < fstab->num_entries; ++i) {
-        const auto fsrec = &fstab->recs[i];
-        const auto fs_type = fsrec->fs_type;
-        if (!fs_type) continue;
-        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
-        auto fsrec_mount_point = fsrec->mount_point;
-        if (!fsrec_mount_point) continue;
-        if (mount_point != fsrec_mount_point) continue;
-        const auto fs_options = fsrec->fs_options;
-        if (!fs_options) continue;
-        const auto options = android::base::Split(fs_options, ",");
-        for (const auto opt : options) {
-            if (opt == lowerdir) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
 std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
                                                const char* mount_point = nullptr) {
     std::vector<std::string> mounts;
@@ -398,16 +400,14 @@
 
 }  // namespace
 
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
     auto ret = false;
 
     if (!fs_mgr_wants_overlayfs()) return ret;
 
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                      fs_mgr_free_fstab);
     if (!fstab) return ret;
 
-    for (const auto& mount_point : fs_mgr_candidate_list(fstab.get())) {
+    for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
         if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
         if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
     }
@@ -430,16 +430,16 @@
         return ret;
     }
 
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                      fs_mgr_free_fstab);
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
     if (fstab && !fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
     auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(fstab.get(), mount_point));
     if (fstab && mounts.empty()) return ret;
 
-    if (setfscreatecon(overlayfs_file_context)) {
-        PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        PERROR << "overlayfs setfscreatecon " << kOverlayfsFileContext;
     }
-    auto overlay = kOverlayMountPoint + "/overlay/";
+    auto overlay = kOverlayMountPoint + kOverlayTopDir;
     auto save_errno = errno;
     if (!mkdir(overlay.c_str(), 0755)) {
         if (change) *change = true;
@@ -462,13 +462,13 @@
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
     if (change) *change = false;
-    mount_point = fs_mgr_mount_point(std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)>(
+    mount_point = fs_mgr_mount_point(std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>(
                                              fs_mgr_read_fstab_default(), fs_mgr_free_fstab)
                                              .get(),
                                      mount_point);
     auto ret = true;
-    const auto overlay = kOverlayMountPoint + "/overlay";
-    const auto oldpath = overlay + (mount_point ?: "");
+    const auto overlay = kOverlayMountPoint + kOverlayTopDir;
+    const auto oldpath = overlay + (mount_point ? "/"s + mount_point : ""s);
     const auto newpath = oldpath + ".teardown";
     ret &= fs_mgr_rm_all(newpath);
     auto save_errno = errno;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index ebc4a0f..506e81d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -113,6 +113,8 @@
 #define MF_KEYDIRECTORY    0X4000000
 #define MF_SYSFS           0X8000000
 #define MF_LOGICAL        0x10000000
+#define MF_CHECKPOINT_BLK 0x20000000
+#define MF_CHECKPOINT_FS  0x40000000
 // clang-format on
 
 #define DM_BUF_SIZE 4096
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 1049fb6..cee069b 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -70,6 +70,8 @@
 
 int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
+int fs_mgr_do_mount(struct fstab* fstab, const char* n_name, char* n_blk_device,
+                    char* tmp_mount_point, bool need_cp);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index ceb45de..251dd9b 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -20,7 +20,7 @@
 
 #include <string>
 
-bool fs_mgr_overlayfs_mount_all();
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index b1ee328..bb40511 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -86,6 +86,9 @@
 int fs_mgr_is_latemount(const struct fstab_rec* fstab);
 int fs_mgr_is_quota(const struct fstab_rec* fstab);
 int fs_mgr_is_logical(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab);
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 018c280..352647b 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -482,6 +482,14 @@
     return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
 }
 
+uint64_t MetadataBuilder::UsedSpace() const {
+    uint64_t size = 0;
+    for (const auto& partition : partitions_) {
+        size += partition->size();
+    }
+    return size;
+}
+
 uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index da9c8f3..0c7e43d 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -202,14 +202,28 @@
 }
 
 TEST(liblp, UseAllDiskSpace) {
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-    EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+    static constexpr uint64_t total = 1024 * 1024;
+    static constexpr uint64_t metadata = 1024;
+    static constexpr uint64_t slots = 2;
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(total, metadata, slots);
+    // We reserve a geometry block (4KB) plus space for each copy of the
+    // maximum size of a metadata blob. Then, we double that space since
+    // we store a backup copy of everything.
+    static constexpr uint64_t geometry = 4 * 1024;
+    static constexpr uint64_t allocatable = total - (metadata * slots + geometry) * 2;
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), 0);
 
     Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
-    EXPECT_EQ(builder->ResizePartition(system, 1036288), true);
-    EXPECT_EQ(system->size(), 1036288);
-    EXPECT_EQ(builder->ResizePartition(system, 1036289), false);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable), true);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable + 1), false);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
 }
 
 TEST(liblp, BuildComplex) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 38842a4..2780825 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -184,6 +184,7 @@
 
     // Amount of space that can be allocated to logical partitions.
     uint64_t AllocatableSpace() const;
+    uint64_t UsedSpace() const;
 
     // Merge new block device information into previous values. Alignment values
     // are only overwritten if the new values are non-zero.
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 329a901..eda68fd 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -117,6 +117,9 @@
     uint64_t size;
     ASSERT_TRUE(GetDescriptorSize(fd, &size));
     ASSERT_EQ(size, kDiskSize);
+
+    // Verify that we can't read unwritten metadata.
+    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
 }
 
 // Flashing metadata should not work if the metadata was created for a larger
@@ -191,9 +194,6 @@
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
 
-    // Verify that we can't read unwritten metadata.
-    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
-
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
     ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index ad84b22..9dd2745 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -94,7 +94,8 @@
     }
     // Make sure we're writing within the space reserved.
     if (blob->size() > geometry.metadata_max_size) {
-        LERROR << "Logical partition metadata is too large.";
+        LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
+               << geometry.metadata_max_size;
         return false;
     }
 
diff --git a/init/Android.mk b/init/Android.mk
index dc400ad..5554995 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -61,9 +61,9 @@
 # Set up the same mount points on the ramdisk that system-as-root contains.
 LOCAL_POST_INSTALL_CMD := \
     mkdir -p $(TARGET_RAMDISK_OUT)/dev \
-    mkdir -p $(TARGET_RAMDISK_OUT)/mnt \
-    mkdir -p $(TARGET_RAMDISK_OUT)/proc \
-    mkdir -p $(TARGET_RAMDISK_OUT)/sys \
+    $(TARGET_RAMDISK_OUT)/mnt \
+    $(TARGET_RAMDISK_OUT)/proc \
+    $(TARGET_RAMDISK_OUT)/sys \
 
 LOCAL_STATIC_LIBRARIES := \
     libfs_mgr \
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index fa3392e..1f4bec1 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -120,8 +120,14 @@
     return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
 }
 
-static bool inline IsRecoveryMode() {
-    return access("/system/bin/recovery", F_OK) == 0;
+static bool IsRecoveryMode() {
+    static bool force_normal_boot = []() {
+        std::string cmdline;
+        android::base::ReadFileToString("/proc/cmdline", &cmdline);
+        return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+    }();
+
+    return !force_normal_boot && access("/system/bin/recovery", F_OK) == 0;
 }
 
 static inline bool IsDmLinearEnabled() {
@@ -362,14 +368,16 @@
     // this case, we mount system first then pivot to it.  From that point on,
     // we are effectively identical to a system-as-root device.
     auto system_partition =
-            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(),
-                         [](const auto& rec) { return rec->mount_point == "/system"s; });
+            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(), [](const auto& rec) {
+                return rec->mount_point == "/system"s ||
+                       rec->mount_point == "/system_recovery_mount"s;
+            });
     if (system_partition != mount_fstab_recs_.end()) {
         if (!MountPartition(*system_partition)) {
             return false;
         }
 
-        SwitchRoot("/system");
+        SwitchRoot((*system_partition)->mount_point);
 
         mount_fstab_recs_.erase(system_partition);
     }
@@ -380,7 +388,7 @@
         }
     }
 
-    fs_mgr_overlayfs_mount_all();
+    fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
 
     return true;
 }
diff --git a/init/init.cpp b/init/init.cpp
index 3ab0a52..47cfe32 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,6 +41,7 @@
 #include <cutils/android_reboot.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
+#include <selinux/android.h>
 
 #ifndef RECOVERY
 #include <binder/ProcessState.h>
@@ -344,12 +346,12 @@
     if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
         return;
     }
-
-    std::string value = GetProperty("ro.boot.verifiedbootstate", "");
-
-    if (!value.empty()) {
-        property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
-    }
+    import_kernel_cmdline(
+            false, [](const std::string& key, const std::string& value, bool in_qemu) {
+                if (key == "androidboot.verifiedbootstate") {
+                    property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
+                }
+            });
 }
 
 static void export_kernel_boot_props() {
@@ -585,6 +587,43 @@
     android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
 }
 
+static void GlobalSeccomp() {
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
+                                    bool in_qemu) {
+        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+            LOG(FATAL) << "Failed to globally enable seccomp!";
+        }
+    });
+}
+
+static void SetupSelinux(char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+    });
+
+    // Set up SELinux, loading the SELinux policy.
+    SelinuxSetupKernelLogging();
+    SelinuxInitialize();
+
+    // We're in the kernel domain and want to transition to the init domain.  File systems that
+    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+    // but other file systems do.  In particular, this is needed for ramdisks such as the
+    // recovery image for A/B devices.
+    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+    }
+
+    setenv("SELINUX_INITIALIZED", "true", 1);
+
+    const char* path = "/system/bin/init";
+    const char* args[] = {path, nullptr};
+    execv(path, const_cast<char**>(args));
+
+    // execv() only returns if an error happened, in which case we
+    // panic and never return from this function.
+    PLOG(FATAL) << "execv(\"" << path << "\") failed";
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -600,9 +639,16 @@
         InstallRebootSignalHandlers();
     }
 
+    if (getenv("SELINUX_INITIALIZED") == nullptr) {
+        SetupSelinux(argv);
+    }
+
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
+    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+    GlobalSeccomp();
+
     // Set up a session keyring that all processes will have access to. It
     // will hold things like FBE encryption keys. No process should override
     // its session keyring.
@@ -631,6 +677,7 @@
     if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
     // Clean up our environment.
+    unsetenv("SELINUX_INITIALIZED");
     unsetenv("INIT_STARTED_AT");
     unsetenv("INIT_SELINUX_TOOK");
     unsetenv("INIT_AVB_VERSION");
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 466cde3..0c4a110 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <paths.h>
-#include <seccomp_policy.h>
 #include <stdlib.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
@@ -30,11 +29,9 @@
 #include <android-base/logging.h>
 #include <cutils/android_reboot.h>
 #include <private/android_filesystem_config.h>
-#include <selinux/android.h>
 
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
-#include "selinux.h"
 #include "util.h"
 
 using android::base::boot_clock;
@@ -42,15 +39,6 @@
 namespace android {
 namespace init {
 
-static void GlobalSeccomp() {
-    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
-                                    bool in_qemu) {
-        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
-            LOG(FATAL) << "Failed to globally enable seccomp!";
-        }
-    });
-}
-
 int main(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -130,22 +118,6 @@
 
     SetInitAvbVersionInRecovery();
 
-    // Does this need to be done in first stage init or can it be done later?
-    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
-    GlobalSeccomp();
-
-    // Set up SELinux, loading the SELinux policy.
-    SelinuxSetupKernelLogging();
-    SelinuxInitialize();
-
-    // We're in the kernel domain and want to transition to the init domain when we exec second
-    // stage init.  File systems that store SELabels in their xattrs, such as ext4 do not need an
-    // explicit restorecon here, but other file systems do.  In particular, this is needed for
-    // ramdisks such as the recovery image for A/B devices.
-    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
-        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
-    }
-
     static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
     uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
     setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index 1e0db57..c61c210 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -50,7 +50,7 @@
     }
 
     // Key is striped module name to match names in alias file
-    std::size_t start = args[0].find_last_of("/");
+    std::size_t start = args[0].find_last_of('/');
     std::size_t end = args[0].find(".ko:");
     if ((end - start) <= 1) return Error() << "malformed dependency line";
     auto mod_name = args[0].substr(start + 1, (end - start) - 1);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f88121..b84bfd3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -464,6 +464,12 @@
         cmd = ANDROID_RB_RESTART2;
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
+            // adb reboot fastboot should boot into bootloader for devices not
+            // supporting logical partitions.
+            if (reboot_target == "fastboot" &&
+                !android::base::GetBoolProperty("ro.boot.logical_partitions", false)) {
+                reboot_target = "bootloader";
+            }
             // When rebooting to the bootloader notify the bootloader writing
             // also the BCB.
             if (reboot_target == "bootloader") {
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index c2a21d4..092c51c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -62,7 +62,9 @@
 Result<std::string> ReadMessage(int socket) {
     char buffer[kBufferSize] = {};
     auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
-    if (result <= 0) {
+    if (result == 0) {
+        return Error();
+    } else if (result < 0) {
         return ErrnoError();
     }
     return std::string(buffer, result);
@@ -175,6 +177,12 @@
 
         auto init_message = ReadMessage(init_fd_);
         if (!init_message) {
+            if (init_message.error_errno() == 0) {
+                // If the init file descriptor was closed, let's exit quietly. If
+                // this was accidental, init will restart us. If init died, this
+                // avoids calling abort(3) unnecessarily.
+                return;
+            }
             LOG(FATAL) << "Could not read message from init: " << init_message.error();
         }
 
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
index bb143f1..7e05a0a 100644
--- a/init/tokenizer.cpp
+++ b/init/tokenizer.cpp
@@ -1,5 +1,7 @@
 #include "tokenizer.h"
 
+#include <android-base/macros.h>
+
 namespace android {
 namespace init {
 
@@ -106,6 +108,7 @@
                     continue;
                 }
                 x++;
+                FALLTHROUGH_INTENDED;
             case '\n':
                     /* \ <lf> -> line continuation */
                 state->line++;
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index bcc0616..d27feb9 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
+cc_library {
     name: "libgrallocusage",
     vendor_available: true,
     cflags: [
diff --git a/libgrallocusage/OWNERS b/libgrallocusage/OWNERS
new file mode 100644
index 0000000..154dc6d
--- /dev/null
+++ b/libgrallocusage/OWNERS
@@ -0,0 +1,3 @@
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index a8a9a12..383d0e7 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -30,6 +30,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #ifdef __ANDROID__  // includes sys/properties.h which does not exist outside
 #include <cutils/properties.h>
@@ -130,7 +131,7 @@
 
 static bool isLogdwActive() {
   std::string logdwSignature =
-      popenToString("grep /dev/socket/logdw /proc/net/unix");
+      popenToString("grep -a /dev/socket/logdw /proc/net/unix");
   size_t beginning = logdwSignature.find(' ');
   if (beginning == std::string::npos) return true;
   beginning = logdwSignature.find(' ', beginning + 1);
@@ -144,7 +145,7 @@
   end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
   std::string allLogdwEndpoints = popenToString(
-      "grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
+      "grep -a ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
       " ' /proc/net/unix | " +
       "sed -n 's/.* \\([0-9][0-9]*\\)$/ -> socket:[\\1]/p'");
   if (allLogdwEndpoints.length() == 0) return true;
@@ -2516,7 +2517,7 @@
 #endif
         elem.data.string = const_cast<char*>("<unknown>");
         elem.len = strlen(elem.data.string);
-      /* FALLTHRU */
+        FALLTHROUGH_INTENDED;
       case EVENT_TYPE_STRING:
         if (elem.len <= strOutLen) {
           memcpy(strOut, elem.data.string, elem.len);
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 14883f4..8c80f6a 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -73,6 +73,7 @@
 LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+LOCAL_HEADER_LIBRARIES := libbase_headers
 LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
index dcb95c5..ea9514c 100644
--- a/libpixelflinger/buffer.cpp
+++ b/libpixelflinger/buffer.cpp
@@ -18,6 +18,8 @@
 
 #include <assert.h>
 
+#include <android-base/macros.h>
+
 #include "buffer.h"
 
 namespace android {
@@ -266,8 +268,11 @@
     p = downshift_component(p, b,   hbits, lbits,  f->bh, f->bl, 0, 1, -1);
     p = downshift_component(p, a,   hbits, lbits,  f->ah, f->al, 0, 1, -1);
     switch (f->size) {
-    case 1: p |= p << 8;    // fallthrough
-    case 2: p |= p << 16;
+        case 1:
+            p |= p << 8;
+            FALLTHROUGH_INTENDED;
+        case 2:
+            p |= p << 16;
     }
     return p;
 }
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
index a55dfe3..2cbb00f 100644
--- a/libpixelflinger/codeflinger/blending.cpp
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <android-base/macros.h>
 #include <log/log.h>
 
 #include "GGLAssembler.h"
@@ -301,7 +302,7 @@
                 return;
             }                
         }
-        // fall-through...
+        FALLTHROUGH_INTENDED;
     case GGL_ONE_MINUS_DST_COLOR:
     case GGL_DST_COLOR:
     case GGL_ONE_MINUS_SRC_COLOR:
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
new file mode 100644
index 0000000..bfa730a
--- /dev/null
+++ b/libprocessgroup/OWNERS
@@ -0,0 +1,2 @@
+ccross@google.com
+tomcherry@google.com
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1cebb5d..0a42053 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -185,7 +185,7 @@
 
     // Erase all pids that will be killed when we kill the process groups.
     for (auto it = pids.begin(); it != pids.end();) {
-        pid_t pgid = getpgid(pid);
+        pid_t pgid = getpgid(*it);
         if (pgids.count(pgid) == 1) {
             it = pids.erase(it);
         } else {
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 0625db7..fe2f3d6 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -27,6 +27,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/macros.h>
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
 
@@ -145,7 +147,8 @@
         switch (*arg) {
         case '\\':
         case '"':
-            *(current++) = '\\'; // fallthrough
+            *(current++) = '\\';
+            FALLTHROUGH_INTENDED;
         default:
             *(current++) = *(arg++);
         }
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index cd9ef61..0fa1638 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -21,6 +21,7 @@
 #include <type_traits>
 #include <vector>
 
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
 #include <unwindstack/DwarfError.h>
@@ -154,13 +155,15 @@
       break;
     case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
       *cur_pc += value;
-    // Fall through to log the value.
+      FALLTHROUGH_INTENDED;
+      // Fall through to log the value.
     case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
       string += " " + std::to_string(value);
       break;
     case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
       *cur_pc = value;
-    // Fall through to log the value.
+      FALLTHROUGH_INTENDED;
+      // Fall through to log the value.
     case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
       if (std::is_same<AddressType, uint32_t>::value) {
         string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 6061f61..57a780e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -461,6 +461,7 @@
       if (reg == eval_info->cie->return_address_register) {
         eval_info->return_address_undefined = true;
       }
+      break;
     default:
       break;
   }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 1c1bdf7..600c91c 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -59,6 +59,7 @@
         "-Werror",
     ],
     header_libs: [
+        "libbase_headers",
         "libutils_headers",
     ],
     export_header_lib_headers: [
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 3f1e79a..ae10789 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -19,6 +19,8 @@
 
 #include <memory>
 
+#include <android-base/macros.h>
+
 #include <utils/RefBase.h>
 
 #include <utils/CallStack.h>
@@ -479,7 +481,7 @@
     case INITIAL_STRONG_VALUE:
         refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
                 std::memory_order_relaxed);
-        // fall through...
+        FALLTHROUGH_INTENDED;
     case 0:
         refs->mBase->onFirstRef();
     }
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 82f650d..5f0a51f 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -16,8 +16,9 @@
 
 #define LOG_TAG "unicode"
 
-#include <utils/Unicode.h>
+#include <android-base/macros.h>
 #include <limits.h>
+#include <utils/Unicode.h>
 
 #include <log/log.h>
 
@@ -105,8 +106,11 @@
     switch (bytes)
     {   /* note: everything falls through. */
         case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
     }
 }
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index add6e14..f8d1356 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -122,21 +122,36 @@
 #endif
 }
 
+static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
+                             const ZipStringOffset& zip_string_offset) {
+  const ZipString from_offset = zip_string_offset.GetZipString(start);
+  return from_offset == zip_string;
+}
+
+/**
+ * Returns offset of ZipString#name from the start of the central directory in the memory map.
+ * For valid ZipStrings contained in the zip archive mmap, 0 < offset < 0xffffff.
+ */
+static inline uint32_t GetOffset(const uint8_t* name, const uint8_t* start) {
+  CHECK_GT(name, start);
+  CHECK_LT(name, start + 0xffffff);
+  return static_cast<uint32_t>(name - start);
+}
+
 /*
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
-                            const ZipString& name) {
+static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
+                            const ZipString& name, const uint8_t* start) {
   const uint32_t hash = ComputeHash(name);
 
   // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
   uint32_t ent = hash & (hash_table_size - 1);
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
+  while (hash_table[ent].name_offset != 0) {
+    if (isZipStringEqual(start, name, hash_table[ent])) {
       return ent;
     }
-
     ent = (ent + 1) & (hash_table_size - 1);
   }
 
@@ -147,8 +162,8 @@
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
-                         const ZipString& name) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size,
+                         const ZipString& name, const uint8_t* start) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -156,20 +171,26 @@
    * We over-allocated the table, so we're guaranteed to find an empty slot.
    * Further, we guarantee that the hashtable size is not 0.
    */
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
+  while (hash_table[ent].name_offset != 0) {
+    if (isZipStringEqual(start, name, hash_table[ent])) {
       // We've found a duplicate entry. We don't accept it
       ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
       return kDuplicateEntry;
     }
     ent = (ent + 1) & (hash_table_size - 1);
   }
-
-  hash_table[ent].name = name.name;
+  hash_table[ent].name_offset = GetOffset(name.name, start);
   hash_table[ent].name_length = name.name_length;
   return 0;
 }
 
+#if defined(__BIONIC__)
+uint64_t GetOwnerTag(const ZipArchive* archive) {
+  return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
+                                        reinterpret_cast<uint64_t>(archive));
+}
+#endif
+
 ZipArchive::ZipArchive(const int fd, bool assume_ownership)
     : mapped_zip(fd),
       close_file(assume_ownership),
@@ -181,7 +202,7 @@
       hash_table(nullptr) {
 #if defined(__BIONIC__)
   if (assume_ownership) {
-    android_fdsan_exchange_owner_tag(fd, 0, reinterpret_cast<uint64_t>(this));
+    android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
   }
 #endif
 }
@@ -199,7 +220,7 @@
 ZipArchive::~ZipArchive() {
   if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
 #if defined(__BIONIC__)
-    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), reinterpret_cast<uint64_t>(this));
+    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
 #else
     close(mapped_zip.GetFileDescriptor());
 #endif
@@ -209,7 +230,8 @@
 }
 
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
-                                    off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
+                                    off64_t file_length, off64_t read_amount,
+                                    uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
 
   if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
@@ -362,7 +384,7 @@
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
   archive->hash_table =
-      reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
+      reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
   if (archive->hash_table == nullptr) {
     ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
           archive->hash_table_size, sizeof(ZipString));
@@ -418,7 +440,8 @@
     ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
-    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
+    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
+                                     archive->central_directory.GetBasePtr());
     if (add_result != 0) {
       ALOGW("Zip: Error adding entry to hash table %d", add_result);
       return add_result;
@@ -538,7 +561,9 @@
   // Recover the start of the central directory entry from the filename
   // pointer.  The filename is the first entry past the fixed-size data,
   // so we can just subtract back from that.
-  const uint8_t* ptr = archive->hash_table[ent].name;
+  const ZipString from_offset =
+      archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+  const uint8_t* ptr = from_offset.name;
   ptr -= sizeof(CentralDirectoryRecord);
 
   // This is the base of our mmapped region, we have to sanity check that
@@ -648,8 +673,9 @@
       ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
       return kIoError;
     }
-
-    if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
+    const ZipString from_offset =
+        archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+    if (memcmp(from_offset.name, name_buf.data(), nameLen)) {
       return kInconsistentInformation;
     }
 
@@ -747,19 +773,19 @@
     return kInvalidEntryName;
   }
 
-  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
-
+  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
+                                   archive->central_directory.GetBasePtr());
   if (ent < 0) {
     ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
     return ent;
   }
-
   return FindEntry(archive, ent, data);
 }
 
 int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
+    ALOGW("Zip: Null ZipArchiveHandle");
     return kInvalidHandle;
   }
 
@@ -771,19 +797,19 @@
 
   const uint32_t currentOffset = handle->position;
   const uint32_t hash_table_length = archive->hash_table_size;
-  const ZipString* hash_table = archive->hash_table;
-
+  const ZipStringOffset* hash_table = archive->hash_table;
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
-    if (hash_table[i].name != NULL &&
-        (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
-        (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
+    const ZipString from_offset =
+        hash_table[i].GetZipString(archive->central_directory.GetBasePtr());
+    if (hash_table[i].name_offset != 0 &&
+        (handle->prefix.name_length == 0 || from_offset.StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 || from_offset.EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
-        name->name = hash_table[i].name;
+        name->name = from_offset.name;
         name->name_length = hash_table[i].name_length;
       }
-
       return error;
     }
   }
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 0a73300..83cb11f 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -136,6 +136,26 @@
   size_t length_;
 };
 
+/**
+ * More space efficient string representation of strings in an mmaped zipped file than
+ * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
+ * space. ZipString stores a pointer to a string (on 64 bit, 8 bytes) and the length to read from
+ * that pointer, 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting 6 bytes.
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory mapped file instead
+ * of the entire address, consuming 8 bytes with alignment.
+ */
+struct ZipStringOffset {
+  uint32_t name_offset;
+  uint16_t name_length;
+
+  const ZipString GetZipString(const uint8_t* start) const {
+    ZipString zip_string;
+    zip_string.name = start + name_offset;
+    zip_string.name_length = name_length;
+    return zip_string;
+  }
+};
+
 struct ZipArchive {
   // open Zip archive
   mutable MappedZipFile mapped_zip;
@@ -154,7 +174,7 @@
   // allocate so the maximum number entries can never be higher than
   // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
   uint32_t hash_table_size;
-  ZipString* hash_table;
+  ZipStringOffset* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership);
   ZipArchive(void* address, size_t length);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 0f1badb..115b1a3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -16,6 +16,7 @@
 
 #include "logcat.h"
 
+#include <android-base/macros.h>
 #include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
@@ -959,7 +960,7 @@
             case 't':
                 got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
             case 'T':
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
                     char* cp = parseTime(tail_time, optarg);
@@ -1009,7 +1010,7 @@
                     getLogSize = true;
                     break;
                 }
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
 
             case 'G': {
                 char* cp;
@@ -1023,15 +1024,15 @@
                     case 'g':
                     case 'G':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case 'm':
                     case 'M':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case 'k':
                     case 'K':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case '\0':
                         break;
 
@@ -1051,7 +1052,7 @@
                     getPruneList = true;
                     break;
                 }
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
 
             case 'P':
                 setPruneList = optarg;
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index bebcc71..9483bb2 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -31,6 +31,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <log/event_tag_map.h>
@@ -572,13 +573,13 @@
         switch (size_mult[0]) {
             case 'G':
                 full_size *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'M':
                 full_size *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'K':
                 full_size *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'B':
                 break;
             default:
@@ -588,13 +589,13 @@
         switch (consumed_mult[0]) {
             case 'G':
                 full_consumed *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'M':
                 full_consumed *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'K':
                 full_consumed *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'B':
                 break;
             default:
@@ -1241,26 +1242,26 @@
             switch (size_mult[0]) {
                 case 'G':
                     full_size *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'M':
                     full_size *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'K':
                     full_size *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'B':
                     break;
             }
             switch (consumed_mult[0]) {
                 case 'G':
                     full_consumed *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'M':
                     full_consumed *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'K':
                     full_consumed *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'B':
                     break;
             }
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index e4393a3..513c0c3 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -475,9 +475,7 @@
 static int convertKernelPrioToAndroidPrio(int pri) {
     switch (pri & LOG_PRIMASK) {
         case LOG_EMERG:
-        // FALLTHRU
         case LOG_ALERT:
-        // FALLTHRU
         case LOG_CRIT:
             return ANDROID_LOG_FATAL;
 
@@ -488,9 +486,7 @@
             return ANDROID_LOG_WARN;
 
         default:
-        // FALLTHRU
         case LOG_NOTICE:
-        // FALLTHRU
         case LOG_INFO:
             break;
 
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index bce308b..4432f9e 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -110,25 +110,25 @@
  */
 
 struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
-    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
-    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
+    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO/ACPIO image */
+    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
     uint32_t header_size;
 } __attribute__((packed));
 
 /* When the boot image header has a version of 1, the structure of the boot
  * image is as follows:
  *
- * +-----------------+
- * | boot header     | 1 page
- * +-----------------+
- * | kernel          | n pages
- * +-----------------+
- * | ramdisk         | m pages
- * +-----------------+
- * | second stage    | o pages
- * +-----------------+
- * | recovery dtbo   | p pages
- * +-----------------+
+ * +---------------------+
+ * | boot header         | 1 page
+ * +---------------------+
+ * | kernel              | n pages
+ * +---------------------+
+ * | ramdisk             | m pages
+ * +---------------------+
+ * | second stage        | o pages
+ * +---------------------+
+ * | recovery dtbo/acpio | p pages
+ * +---------------------+
  * n = (kernel_size + page_size - 1) / page_size
  * m = (ramdisk_size + page_size - 1) / page_size
  * o = (second_size + page_size - 1) / page_size
@@ -136,13 +136,14 @@
  *
  * 0. all entities are page_size aligned in flash
  * 1. kernel and ramdisk are required (size != 0)
- * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
+ * 2. recovery_dtbo/recovery_acpio is required for recovery.img in non-A/B
+ *    devices(recovery_dtbo_size != 0)
  * 3. second is optional (second_size == 0 -> no second)
  * 4. load each element (kernel, ramdisk, second) at
  *    the specified physical address (kernel_addr, etc)
- * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
- *    apply the correct set of overlays on the base device tree depending on the
- *    hardware/product revision.
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery
+ *    dtbo/acpio and apply the correct set of overlays on the base device tree
+ *    depending on the hardware/product revision.
  * 6. prepare tags at tag_addr.  kernel_args[] is
  *    appended to the kernel commandline in the tags.
  * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index fda9af0..2eb2bab 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -161,7 +161,10 @@
                         required=True)
     parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
     parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
-    parser.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+    recovery_dtbo_group = parser.add_mutually_exclusive_group()
+    recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+    recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
+                                     type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
     parser.add_argument('--cmdline', help='extra arguments to be passed on the '
                         'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
     parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index f2892f8..d3e80c9 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -8,7 +8,6 @@
 dir.system = /system/bin/
 dir.system = /system/xbin/
 dir.system = /%PRODUCT%/bin/
-dir.system = /%PRODUCT_SERVICES%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -78,9 +77,9 @@
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths +=           /product_services/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.permitted.paths  = /data
 namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -345,7 +344,9 @@
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
 namespace.system.asan.search.paths += /data/asan/product/${LIB}
-namespace.system.asan.search.paths +=           /product/${LIB}
+namespace.system.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index db65c14..7e354ac 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,7 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -41,7 +41,8 @@
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
@@ -50,7 +51,9 @@
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 ###############################################################################
 # "sphal" namespace
@@ -209,7 +212,8 @@
 namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
@@ -230,7 +234,9 @@
 namespace.default.asan.search.paths += /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
@@ -243,4 +249,5 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e20b95d..d8f6095 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index ff0813d..20905bf 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -2,6 +2,7 @@
 libandroid.so
 libandroidthings.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 3c46094..4ece5b5 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 915540e..f39ea7c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -407,6 +407,9 @@
     mkdir /data/bootchart 0755 shell shell
     bootchart start
 
+    # Start apexd as soon as we can
+    start apexd
+
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
 
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 1050033..8b5001d 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -129,6 +129,10 @@
 
     bool success = false;
     auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG_TO(SYSTEM, DEBUG) << "getDiskStats is not supported on health HAL.";
+            return;
+        }
         if (result != Result::SUCCESS || halStats.size() == 0) {
             LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
                                   << " and size " << halStats.size();
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 5605f66..8c0b3d1 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -370,8 +370,12 @@
 
 void health_storage_info_t::report() {
     auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
+            return;
+        }
         if (result != Result::SUCCESS || halInfos.size() == 0) {
-            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
+            LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
                                   << " and size " << halInfos.size();
             return;
         }
@@ -380,7 +384,7 @@
     });
 
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
+        LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
     }
 }