Merge "Load *_compat_cil_file from system_ext as well"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index fb274ec..7b6f6c0 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -238,6 +238,7 @@
         "gwp_asan_crash_handler",
         "libscudo",
         "libtombstone_proto",
+        "libprocinfo",
         "libprotobuf-cpp-lite",
     ],
 
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 6bfb5f2..530e0e8 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -96,7 +96,7 @@
 
   if (std::string str = data.str(); !str.empty()) {
     buffer << "\n----- Waiting Channels: pid " << pid << " at " << get_timestamp() << " -----\n"
-           << "Cmd line: " << get_process_name(pid) << "\n";
+           << "Cmd line: " << android::base::Join(get_command_line(pid), " ") << "\n";
     buffer << "\n" << str << "\n";
     buffer << "----- end " << std::to_string(pid) << " -----\n";
     buffer << "\n";
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 04e1e4e..a152740 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -450,9 +450,6 @@
   //       unwind, do not make this too small. b/62828735
   alarm(30 * android::base::HwTimeoutMultiplier());
 
-  // Get the process name (aka cmdline).
-  std::string process_name = get_process_name(g_target_thread);
-
   // Collect the list of open files.
   OpenFilesList open_files;
   {
@@ -489,7 +486,6 @@
       info.pid = target_process;
       info.tid = thread;
       info.uid = getuid();
-      info.process_name = process_name;
       info.thread_name = get_thread_name(thread);
 
       unique_fd attr_fd(openat(target_proc_fd, "attr/current", O_RDONLY | O_CLOEXEC));
@@ -517,6 +513,8 @@
         ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info);
         info.siginfo = &siginfo;
         info.signo = info.siginfo->si_signo;
+
+        info.command_line = get_command_line(g_target_thread);
       } else {
         info.registers.reset(unwindstack::Regs::RemoteGet(thread));
         if (!info.registers) {
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index c543a83..fd91038 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -34,6 +34,7 @@
 #include <memory>
 #include <string>
 
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <log/log.h>
 #include <unwindstack/Unwinder.h>
@@ -42,11 +43,12 @@
 #include "libdebuggerd/utility.h"
 #include "util.h"
 
-static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
+static void dump_process_header(log_t* log, pid_t pid,
+                                const std::vector<std::string>& command_line) {
   _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, get_timestamp().c_str());
 
-  if (process_name) {
-    _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", process_name);
+  if (!command_line.empty()) {
+    _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", android::base::Join(command_line, " ").c_str());
   }
   _LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
 }
@@ -89,7 +91,7 @@
     return;
   }
 
-  dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
+  dump_process_header(&log, target->second.pid, target->second.command_line);
 
   dump_backtrace_thread(output_fd.get(), unwinder, target->second);
   for (const auto& [tid, info] : thread_info) {
@@ -107,7 +109,7 @@
   log.amfd_data = nullptr;
 
   pid_t pid = getpid();
-  dump_process_header(&log, pid, get_process_name(pid).c_str());
+  dump_process_header(&log, pid, get_command_line(pid));
 }
 
 void dump_backtrace_footer(int output_fd) {
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index dcb52f9..086dc97 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -18,6 +18,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <unwindstack/Regs.h>
 
@@ -32,13 +33,14 @@
 
   pid_t pid;
 
-  std::string process_name;
+  std::vector<std::string> command_line;
   std::string selinux_label;
 
   int signo = 0;
   siginfo_t* siginfo = nullptr;
 };
 
+// This struct is written into a pipe from inside the crashing process.
 struct ProcessInfo {
   uintptr_t abort_msg_address = 0;
   uintptr_t fdsan_table_address = 0;
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
index 44a9214..8f84346 100644
--- a/debuggerd/libdebuggerd/test/UnwinderMock.h
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -33,8 +33,7 @@
   void MockSetBuildID(uint64_t offset, const std::string& build_id) {
     unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
     if (map_info != nullptr) {
-      std::string* new_build_id = new std::string(build_id);
-      map_info->build_id = new_build_id;
+      map_info->SetBuildID(std::string(build_id));
     }
   }
 };
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 79ac122..a14dcb0 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -350,11 +350,11 @@
 }
 
 TEST_F(TombstoneTest, dump_thread_info_uid) {
-  dump_thread_info(&log_, ThreadInfo{.uid = 1,
-                                     .tid = 3,
-                                     .thread_name = "some_thread",
-                                     .pid = 2,
-                                     .process_name = "some_process"});
+  std::vector<std::string> cmdline = {"some_process"};
+  dump_thread_info(
+      &log_,
+      ThreadInfo{
+          .uid = 1, .tid = 3, .thread_name = "some_thread", .pid = 2, .command_line = cmdline});
   std::string expected = "pid: 2, tid: 3, name: some_thread  >>> some_process <<<\nuid: 1\n";
   ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 4f75ff1..e0cc662 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -182,8 +182,13 @@
   // Don't try to collect logs from the threads that implement the logging system itself.
   if (thread_info.uid == AID_LOGD) log->should_retrieve_logcat = false;
 
+  const char* process_name = "<unknown>";
+  if (!thread_info.command_line.empty()) {
+    process_name = thread_info.command_line[0].c_str();
+  }
+
   _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", thread_info.pid,
-       thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
+       thread_info.tid, thread_info.thread_name.c_str(), process_name);
   _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
   if (thread_info.tagged_addr_ctrl != -1) {
     _LOG(log, logtype::HEADER, "tagged_addr_ctrl: %016lx\n", thread_info.tagged_addr_ctrl);
@@ -567,7 +572,7 @@
   log.amfd_data = nullptr;
 
   std::string thread_name = get_thread_name(tid);
-  std::string process_name = get_process_name(pid);
+  std::vector<std::string> command_line = get_command_line(pid);
 
   std::unique_ptr<unwindstack::Regs> regs(
       unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
@@ -582,7 +587,7 @@
       .tid = tid,
       .thread_name = std::move(thread_name),
       .pid = pid,
-      .process_name = std::move(process_name),
+      .command_line = std::move(command_line),
       .selinux_label = std::move(selinux_label),
       .siginfo = siginfo,
   };
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 1ab93fe..7657001 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -31,10 +31,12 @@
 #include <time.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include <async_safe/log.h>
 
+#include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -47,6 +49,7 @@
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 
+#include <procinfo/process.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -519,6 +522,14 @@
   dump_log_file(tombstone, "main", pid);
 }
 
+static std::optional<uint64_t> read_uptime_secs() {
+  std::string uptime;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
+    return {};
+  }
+  return strtoll(uptime.c_str(), nullptr, 10);
+}
+
 void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                              const ProcessInfo& process_info, const OpenFilesList* open_files) {
@@ -529,13 +540,33 @@
   result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
   result.set_timestamp(get_timestamp());
 
+  std::optional<uint64_t> system_uptime = read_uptime_secs();
+  if (system_uptime) {
+    android::procinfo::ProcessInfo proc_info;
+    std::string error;
+    if (android::procinfo::GetProcessInfo(target_thread, &proc_info, &error)) {
+      uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
+      result.set_process_uptime(*system_uptime - starttime);
+    } else {
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
+                            error.c_str());
+    }
+  } else {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read /proc/uptime: %s",
+                          strerror(errno));
+  }
+
   const ThreadInfo& main_thread = threads.at(target_thread);
   result.set_pid(main_thread.pid);
   result.set_tid(main_thread.tid);
   result.set_uid(main_thread.uid);
   result.set_selinux_label(main_thread.selinux_label);
 
-  result.set_process_name(main_thread.process_name);
+  auto cmd_line = result.mutable_command_line();
+  for (const auto& arg : main_thread.command_line) {
+    *cmd_line->Add() = arg;
+  }
+
   if (!main_thread.siginfo) {
     async_safe_fatal("siginfo missing");
   }
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index e4dd6df..020b0a5 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 
@@ -71,8 +72,13 @@
 
 static void print_thread_header(CallbackType callback, const Tombstone& tombstone,
                                 const Thread& thread, bool should_log) {
+  const char* process_name = "<unknown>";
+  if (!tombstone.command_line().empty()) {
+    process_name = tombstone.command_line()[0].c_str();
+    CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
+  }
   CB(should_log, "pid: %d, tid: %d, name: %s  >>> %s <<<", tombstone.pid(), thread.id(),
-     thread.name().c_str(), tombstone.process_name().c_str());
+     thread.name().c_str(), process_name);
   CB(should_log, "uid: %d", tombstone.uid());
   if (thread.tagged_addr_ctrl() != -1) {
     CB(should_log, "tagged_addr_ctrl: %016" PRIx64, thread.tagged_addr_ctrl());
@@ -360,6 +366,7 @@
   CBL("Revision: '%s'", tombstone.revision().c_str());
   CBL("ABI: '%s'", abi_string(tombstone));
   CBL("Timestamp: %s", tombstone.timestamp().c_str());
+  CBL("Process uptime: %ds", tombstone.process_uptime());
 
   // Process header
   const auto& threads = tombstone.threads();
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 433c406..294e4e5 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -17,7 +17,10 @@
   uint32 uid = 7;
   string selinux_label = 8;
 
-  string process_name = 9;
+  repeated string command_line = 9;
+
+  // Process uptime in seconds.
+  uint32 process_uptime = 20;
 
   Signal signal_info = 10;
   string abort_message = 14;
@@ -28,7 +31,7 @@
   repeated LogBuffer log_buffers = 18;
   repeated FD open_fds = 19;
 
-  reserved 20 to 999;
+  reserved 21 to 999;
 }
 
 enum Architecture {
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index f3bff8c..ce0fd30 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -26,10 +26,31 @@
 #include <android-base/strings.h>
 #include "protocol.h"
 
+std::vector<std::string> get_command_line(pid_t pid) {
+  std::vector<std::string> result;
+
+  std::string cmdline;
+  android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &cmdline);
+
+  auto it = cmdline.cbegin();
+  while (it != cmdline.cend()) {
+    // string::iterator is a wrapped type, not a raw char*.
+    auto terminator = std::find(it, cmdline.cend(), '\0');
+    result.emplace_back(it, terminator);
+    it = std::find_if(terminator, cmdline.cend(), [](char c) { return c != '\0'; });
+  }
+  if (result.empty()) {
+    result.emplace_back("<unknown>");
+  }
+
+  return result;
+}
+
 std::string get_process_name(pid_t pid) {
   std::string result = "<unknown>";
   android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
-  return result;
+  // We only want the name, not the whole command line, so truncate at the first NUL.
+  return result.c_str();
 }
 
 std::string get_thread_name(pid_t tid) {
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 07e7e99..ec2862a 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -17,10 +17,12 @@
 #pragma once
 
 #include <string>
+#include <vector>
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+std::vector<std::string> get_command_line(pid_t pid);
 std::string get_process_name(pid_t pid);
 std::string get_thread_name(pid_t tid);
 
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 3c4269b..ce702a0 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -127,6 +127,7 @@
         "-Wextra",
         "-Werror",
         "-Wvla",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
     ],
     rtti: true,
 
@@ -182,6 +183,7 @@
     ],
 
     static_libs: [
+        "libc++fs",
         "libgtest_prod",
         "libhealthhalutils",
         "libsnapshot_cow",
@@ -206,6 +208,8 @@
         "-Wextra",
         "-Werror",
         "-Wunreachable-code",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+        "-D_FILE_OFFSET_BITS=64"
     ],
 
     target: {
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 2c0989e..d2056aa 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -34,22 +34,22 @@
 #include <stdlib.h>
 #include <string.h>
 
-static void bootimg_set_cmdline_v3(boot_img_hdr_v3* h, const std::string& cmdline) {
+static void bootimg_set_cmdline_v3_and_above(boot_img_hdr_v3* h, const std::string& cmdline) {
     if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
     strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
 void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {
-    if (h->header_version == 3) {
-        return bootimg_set_cmdline_v3(reinterpret_cast<boot_img_hdr_v3*>(h), cmdline);
+    if (h->header_version >= 3) {
+        return bootimg_set_cmdline_v3_and_above(reinterpret_cast<boot_img_hdr_v3*>(h), cmdline);
     }
     if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
     strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
-static boot_img_hdr_v3* mkbootimg_v3(const std::vector<char>& kernel,
-                                     const std::vector<char>& ramdisk, const boot_img_hdr_v2& src,
-                                     std::vector<char>* out) {
+static void mkbootimg_v3_and_above(const std::vector<char>& kernel,
+                                   const std::vector<char>& ramdisk, const boot_img_hdr_v2& src,
+                                   std::vector<char>* out) {
 #define V3_PAGE_SIZE 4096
     const size_t page_mask = V3_PAGE_SIZE - 1;
     int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
@@ -65,22 +65,27 @@
     hdr->ramdisk_size = ramdisk.size();
     hdr->os_version = src.os_version;
     hdr->header_size = sizeof(boot_img_hdr_v3);
-    hdr->header_version = 3;
+    hdr->header_version = src.header_version;
+
+    if (src.header_version >= 4) {
+        auto hdr_v4 = reinterpret_cast<boot_img_hdr_v4*>(hdr);
+        hdr_v4->signature_size = 0;
+    }
 
     memcpy(hdr->magic + V3_PAGE_SIZE, kernel.data(), kernel.size());
     memcpy(hdr->magic + V3_PAGE_SIZE + kernel_actual, ramdisk.data(), ramdisk.size());
-
-    return hdr;
 }
 
-boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
-                           const std::vector<char>& second, const std::vector<char>& dtb,
-                           size_t base, const boot_img_hdr_v2& src, std::vector<char>* out) {
-    if (src.header_version == 3) {
+void mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+               const std::vector<char>& second, const std::vector<char>& dtb, size_t base,
+               const boot_img_hdr_v2& src, std::vector<char>* out) {
+    if (src.header_version >= 3) {
         if (!second.empty() || !dtb.empty()) {
-            die("Second stage bootloader and dtb not supported in v3 boot image\n");
+            die("Second stage bootloader and dtb not supported in v%d boot image\n",
+                src.header_version);
         }
-        return reinterpret_cast<boot_img_hdr_v2*>(mkbootimg_v3(kernel, ramdisk, src, out));
+        mkbootimg_v3_and_above(kernel, ramdisk, src, out);
+        return;
     }
     const size_t page_mask = src.page_size - 1;
 
@@ -122,5 +127,4 @@
            second.size());
     memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual + second_actual, dtb.data(),
            dtb.size());
-    return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index b7cf9bd..0eb003d 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -35,7 +35,8 @@
 #include <string>
 #include <vector>
 
-boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
-                           const std::vector<char>& second, const std::vector<char>& dtb,
-                           size_t base, const boot_img_hdr_v2& src, std::vector<char>* out);
+void mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+               const std::vector<char>& second, const std::vector<char>& dtb, size_t base,
+               const boot_img_hdr_v2& src, std::vector<char>* out);
+
 void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline);
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b72f3fe..0a72812 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -392,13 +392,13 @@
 
     struct sockaddr_un addr = {.sun_family = AF_UNIX};
     strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
-    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+    if (connect(sock.get(), (struct sockaddr*)&addr, sizeof(addr)) < 0) {
         PLOG(ERROR) << "Couldn't connect to recovery";
         return false;
     }
     // Switch to recovery will not update the boot reason since it does not
     // require a reboot.
-    auto ret = write(sock, &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
+    auto ret = write(sock.get(), &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
     if (ret != sizeof(msg_switch_to_recovery)) {
         PLOG(ERROR) << "Couldn't write message to switch to recovery";
         return false;
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index 4bee7b2..4115a6d 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -82,7 +82,7 @@
     int orig_len = len;
     while (len > 0) {
         int write_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = write(h->bulk_in, buf, write_len);
+        int n = write(h->bulk_in.get(), buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
             return -1;
@@ -103,7 +103,7 @@
     unsigned count = 0;
     while (len > 0) {
         int read_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = read(h->bulk_out, buf, read_len);
+        int n = read(h->bulk_out.get(), buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
             return -1;
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index f9267e0..07ad902 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -204,12 +204,7 @@
 }
 
 bool GetDeviceLockStatus() {
-    std::string cmdline;
-    // Return lock status true if unable to read kernel command line.
-    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        return true;
-    }
-    return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
+    return android::base::GetProperty("ro.boot.verifiedbootstate", "") != "orange";
 }
 
 bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e1bf984..e5319a5 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -230,9 +230,9 @@
     fprintf(stderr, "(bootloader) %s\n", info.c_str());
 }
 
-static int64_t get_file_size(int fd) {
+static int64_t get_file_size(borrowed_fd fd) {
     struct stat sb;
-    if (fstat(fd, &sb) == -1) {
+    if (fstat(fd.get(), &sb) == -1) {
         die("could not get file size");
     }
     return sb.st_size;
@@ -411,6 +411,13 @@
             " gsi wipe|disable           Wipe or disable a GSI installation (fastbootd only).\n"
             " wipe-super [SUPER_EMPTY]   Wipe the super partition. This will reset it to\n"
             "                            contain an empty set of default dynamic partitions.\n"
+            " create-logical-partition NAME SIZE\n"
+            "                            Create a logical partition with the given name and\n"
+            "                            size, in the super partition.\n"
+            " delete-logical-partition NAME\n"
+            "                            Delete a logical partition with the given name.\n"
+            " resize-logical-partition NAME SIZE\n"
+            "                            Change the size of the named logical partition.\n"
             " snapshot-update cancel     On devices that support snapshot-based updates, cancel\n"
             "                            an in-progress update. This may make the device\n"
             "                            unbootable until it is reflashed.\n"
@@ -523,10 +530,12 @@
     fprintf(stderr,"creating boot image...\n");
 
     std::vector<char> out;
-    boot_img_hdr_v2* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
-                                                 dtb_data, g_base_addr, g_boot_img_hdr, &out);
+    mkbootimg(kernel_data, ramdisk_data, second_stage_data, dtb_data, g_base_addr, g_boot_img_hdr,
+              &out);
 
-    if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
+    if (!g_cmdline.empty()) {
+        bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v2*>(out.data()), g_cmdline);
+    }
     fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
     return out;
 }
@@ -657,12 +666,12 @@
     fprintf(stderr, "extracting %s (%" PRIu64 " MB) to disk...", entry_name,
             zip_entry.uncompressed_length / 1024 / 1024);
     double start = now();
-    int error = ExtractEntryToFile(zip, &zip_entry, fd);
+    int error = ExtractEntryToFile(zip, &zip_entry, fd.get());
     if (error != 0) {
         die("\nfailed to extract '%s': %s", entry_name, ErrorCodeString(error));
     }
 
-    if (lseek(fd, 0, SEEK_SET) != 0) {
+    if (lseek(fd.get(), 0, SEEK_SET) != 0) {
         die("\nlseek on extracted file '%s' failed: %s", entry_name, strerror(errno));
     }
 
@@ -671,7 +680,7 @@
     return fd;
 }
 
-static void CheckRequirement(const std::string& cur_product, const std::string& var,
+static bool 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 + "'");
@@ -683,7 +692,7 @@
             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 true;
         }
     }
 
@@ -692,7 +701,7 @@
         fprintf(stderr, "FAILED\n\n");
         fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
                 fb->Error().c_str());
-        die("requirements not met!");
+        return false;
     }
 
     bool match = false;
@@ -712,7 +721,7 @@
     if (match) {
         double split = now();
         fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
-        return;
+        return true;
     }
 
     fprintf(stderr, "FAILED\n\n");
@@ -722,7 +731,7 @@
         fprintf(stderr, " or '%s'", it->c_str());
     }
     fprintf(stderr, ".\n\n");
-    die("requirements not met!");
+    return false;
 }
 
 bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
@@ -786,7 +795,7 @@
     }
 }
 
-static void CheckRequirements(const std::string& data) {
+static void CheckRequirements(const std::string& data, bool force_flash) {
     std::string cur_product;
     if (fb->GetVar("product", &cur_product) != fastboot::SUCCESS) {
         fprintf(stderr, "getvar:product FAILED (%s)\n", fb->Error().c_str());
@@ -810,7 +819,14 @@
         if (name == "partition-exists") {
             HandlePartitionExists(options);
         } else {
-            CheckRequirement(cur_product, name, product, invert, options);
+            bool met = CheckRequirement(cur_product, name, product, invert, options);
+            if (!met) {
+                if (!force_flash) {
+                  die("requirements not met!");
+                } else {
+                  fprintf(stderr, "requirements not met! but proceeding due to --force\n");
+                }
+            }
         }
     }
 }
@@ -902,17 +918,18 @@
         return false;
     }
 
-    if (sparse_file* s = sparse_file_import(fd, false, false)) {
+    if (sparse_file* s = sparse_file_import(fd.get(), false, false)) {
         buf->image_size = sparse_file_len(s, false, false);
         sparse_file_destroy(s);
     } else {
         buf->image_size = sz;
     }
 
-    lseek(fd, 0, SEEK_SET);
+    lseek(fd.get(), 0, SEEK_SET);
     int64_t limit = get_sparse_limit(sz);
+    buf->fd = std::move(fd);
     if (limit) {
-        sparse_file** s = load_sparse_files(fd, limit);
+        sparse_file** s = load_sparse_files(buf->fd.get(), limit);
         if (s == nullptr) {
             return false;
         }
@@ -921,7 +938,6 @@
     } else {
         buf->type = FB_BUFFER_FD;
         buf->data = nullptr;
-        buf->fd = std::move(fd);
         buf->sz = sz;
     }
 
@@ -936,7 +952,7 @@
     }
 
     struct stat s;
-    if (fstat(fd, &s)) {
+    if (fstat(fd.get(), &s)) {
         return false;
     }
     if (!S_ISREG(s.st_mode)) {
@@ -995,7 +1011,7 @@
         die("Failed writing to modified vbmeta");
     }
     buf->fd = std::move(fd);
-    lseek(buf->fd, 0, SEEK_SET);
+    lseek(buf->fd.get(), 0, SEEK_SET);
 }
 
 static bool has_vbmeta_partition() {
@@ -1005,6 +1021,11 @@
            fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
 }
 
+static bool is_logical(const std::string& partition) {
+    std::string value;
+    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+}
+
 static std::string fb_fix_numeric_var(std::string var) {
     // Some bootloaders (angler, for example), send spurious leading whitespace.
     var = android::base::Trim(var);
@@ -1017,12 +1038,18 @@
 static uint64_t get_partition_size(const std::string& partition) {
     std::string partition_size_str;
     if (fb->GetVar("partition-size:" + partition, &partition_size_str) != fastboot::SUCCESS) {
+        if (!is_logical(partition)) {
+            return 0;
+        }
         die("cannot get partition size for %s", partition.c_str());
     }
 
     partition_size_str = fb_fix_numeric_var(partition_size_str);
     uint64_t partition_size;
     if (!android::base::ParseUint(partition_size_str, &partition_size)) {
+        if (!is_logical(partition)) {
+            return 0;
+        }
         die("Couldn't parse partition size '%s'.", partition_size_str.c_str());
     }
     return partition_size;
@@ -1033,16 +1060,6 @@
         return;
     }
 
-    // If overflows and negative, it should be < buf->sz.
-    int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
-
-    if (partition_size == buf->sz) {
-        return;
-    }
-    if (partition_size < buf->sz) {
-        die("boot partition is smaller than boot image");
-    }
-
     std::string data;
     if (!android::base::ReadFdToString(buf->fd, &data)) {
         die("Failed reading from boot");
@@ -1052,18 +1069,27 @@
     if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
         return;
     }
+    // If overflows and negative, it should be < buf->sz.
+    int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
+
+    if (partition_size == buf->sz) {
+        return;
+    }
+    if (partition_size < buf->sz) {
+        die("boot partition is smaller than boot image");
+    }
 
     unique_fd fd(make_temporary_fd("boot rewriting"));
     if (!android::base::WriteStringToFd(data, fd)) {
         die("Failed writing to modified boot");
     }
-    lseek(fd, partition_size - AVB_FOOTER_SIZE, SEEK_SET);
+    lseek(fd.get(), partition_size - AVB_FOOTER_SIZE, SEEK_SET);
     if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
         die("Failed copying AVB footer in boot");
     }
     buf->fd = std::move(fd);
     buf->sz = partition_size;
-    lseek(buf->fd, 0, SEEK_SET);
+    lseek(buf->fd.get(), 0, SEEK_SET);
 }
 
 static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
@@ -1248,11 +1274,6 @@
     }
 }
 
-static bool is_logical(const std::string& partition) {
-    std::string value;
-    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
-}
-
 static bool is_retrofit_device() {
     std::string value;
     if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
@@ -1391,13 +1412,15 @@
 
 class ImageSource {
   public:
+    virtual ~ImageSource() {};
     virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
     virtual unique_fd OpenFile(const std::string& name) const = 0;
 };
 
 class FlashAllTool {
   public:
-    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe);
+    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
+                 bool wipe, bool force_flash);
 
     void Flash();
 
@@ -1413,16 +1436,19 @@
     std::string slot_override_;
     bool skip_secondary_;
     bool wipe_;
+    bool force_flash_;
     std::string secondary_slot_;
     std::vector<std::pair<const Image*, std::string>> boot_images_;
     std::vector<std::pair<const Image*, std::string>> os_images_;
 };
 
-FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe)
+FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
+                           bool skip_secondary, bool wipe, bool force_flash)
    : source_(source),
      slot_override_(slot_override),
      skip_secondary_(skip_secondary),
-     wipe_(wipe)
+     wipe_(wipe),
+     force_flash_(force_flash)
 {
 }
 
@@ -1470,7 +1496,7 @@
     if (!source_.ReadFile("android-info.txt", &contents)) {
         die("could not read android-info.txt");
     }
-    ::CheckRequirements({contents.data(), contents.size()});
+    ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
 }
 
 void FlashAllTool::DetermineSecondarySlot() {
@@ -1538,7 +1564,7 @@
 }
 
 void FlashAllTool::UpdateSuperPartition() {
-    int fd = source_.OpenFile("super_empty.img");
+    unique_fd fd = source_.OpenFile("super_empty.img");
     if (fd < 0) {
         return;
     }
@@ -1590,14 +1616,15 @@
     return unzip_to_file(zip_, name.c_str());
 }
 
-static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary,
+                      bool force_flash) {
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
-    FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false);
+    FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false, force_flash);
     tool.Flash();
 
     CloseArchive(zip);
@@ -1622,8 +1649,9 @@
     return unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
 }
 
-static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
-    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
+static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe,
+                        bool force_flash) {
+    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe, force_flash);
     tool.Flash();
 }
 
@@ -2171,9 +2199,9 @@
         } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(slot_override, true, wants_wipe);
+                do_flashall(slot_override, true, wants_wipe, force_flash);
             } else {
-                do_flashall(slot_override, skip_secondary, wants_wipe);
+                do_flashall(slot_override, skip_secondary, wants_wipe, force_flash);
             }
             wants_reboot = true;
         } else if (command == "update") {
@@ -2185,7 +2213,7 @@
             if (!args.empty()) {
                 filename = next_arg(&args);
             }
-            do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
+            do_update(filename.c_str(), slot_override, skip_secondary || slot_all, force_flash);
             wants_reboot = true;
         } else if (command == FB_CMD_SET_ACTIVE) {
             std::string slot = verify_slot(next_arg(&args), false);
@@ -2197,7 +2225,7 @@
             if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
-            fb->Download(filename, buf.fd, buf.sz);
+            fb->Download(filename, buf.fd.get(), buf.sz);
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
             fb->Upload(filename);
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 14ee785..99a4873 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -144,7 +144,8 @@
     return Flash(partition);
 }
 
-RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
+                                       uint32_t size) {
     RetCode ret;
     if ((ret = Download(partition, fd, size))) {
         return ret;
@@ -182,15 +183,16 @@
     return SUCCESS;
 }
 
-RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
-                                 std::string* response, std::vector<std::string>* info) {
+RetCode FastBootDriver::Download(const std::string& name, android::base::borrowed_fd fd,
+                                 size_t size, std::string* response,
+                                 std::vector<std::string>* info) {
     prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
     auto result = Download(fd, size, response, info);
     epilog_(result);
     return result;
 }
 
-RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
+RetCode FastBootDriver::Download(android::base::borrowed_fd fd, size_t size, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
 
@@ -521,7 +523,7 @@
 }
 
 /******************************* PRIVATE **************************************/
-RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
+RetCode FastBootDriver::SendBuffer(android::base::borrowed_fd fd, size_t size) {
     static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
     off64_t offset = 0;
     uint32_t remaining = size;
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index f1c094f..bccd668 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -77,9 +77,9 @@
     RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode CreatePartition(const std::string& partition, const std::string& size);
     RetCode DeletePartition(const std::string& partition);
-    RetCode Download(const std::string& name, int fd, size_t size, std::string* response = nullptr,
-                     std::vector<std::string>* info = nullptr);
-    RetCode Download(int fd, size_t size, std::string* response = nullptr,
+    RetCode Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
+                     std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Download(android::base::borrowed_fd fd, size_t size, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Download(const std::string& name, const std::vector<char>& buf,
                      std::string* response = nullptr, std::vector<std::string>* info = nullptr);
@@ -113,7 +113,8 @@
 
     /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
     RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
-    RetCode FlashPartition(const std::string& partition, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
+                           uint32_t sz);
     RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
                            size_t current, size_t total);
 
@@ -149,7 +150,7 @@
     Transport* transport_;
 
   private:
-    RetCode SendBuffer(int fd, size_t size);
+    RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);
     RetCode SendBuffer(const std::vector<char>& buf);
     RetCode SendBuffer(const void* buf, size_t size);
 
diff --git a/fastboot/testdata/Android.bp b/fastboot/testdata/Android.bp
index 5debf5e..a490fe2 100644
--- a/fastboot/testdata/Android.bp
+++ b/fastboot/testdata/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 python_binary_host {
     name: "fastboot_gen_rand",
     visibility: [":__subpackages__"],
diff --git a/fastboot/vendor_boot_img_utils.cpp b/fastboot/vendor_boot_img_utils.cpp
index 2db20cd..9e09abb 100644
--- a/fastboot/vendor_boot_img_utils.cpp
+++ b/fastboot/vendor_boot_img_utils.cpp
@@ -184,7 +184,7 @@
     if (!android::base::WriteStringToFd(data, fd)) {
         return ErrnoErrorf("Cannot write new content to {}", what);
     }
-    if (TEMP_FAILURE_RETRY(ftruncate64(fd.get(), data.size())) == -1) {
+    if (TEMP_FAILURE_RETRY(ftruncate(fd.get(), data.size())) == -1) {
         return ErrnoErrorf("Truncating new vendor boot image to 0x{:x} fails", data.size());
     }
     return {};
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6952cdf..bbbb7e8 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -647,6 +647,46 @@
     return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
 }
 
+static void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {
+    std::string block_device;
+    if (!Realpath(entry_block_device, &block_device)) {
+        PERROR << "Failed to realpath " << entry_block_device;
+        return;
+    }
+
+    static constexpr std::string_view kDevBlockPrefix("/dev/block/");
+    if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {
+        LWARNING << block_device << " is not a block device";
+        return;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    while (true) {
+        std::string block_name = block_device;
+        if (android::base::StartsWith(block_device, kDevBlockPrefix)) {
+            block_name = block_device.substr(kDevBlockPrefix.length());
+        }
+        std::string sys_partition =
+                android::base::StringPrintf("/sys/class/block/%s/partition", block_name.c_str());
+        struct stat info;
+        if (lstat(sys_partition.c_str(), &info) == 0) {
+            // it has a partition like "sda12".
+            block_name += "/..";
+        }
+        std::string sys_ra = android::base::StringPrintf("/sys/class/block/%s/queue/read_ahead_kb",
+                                                         block_name.c_str());
+        std::string size = android::base::StringPrintf("%llu", (long long)size_kb);
+        android::base::WriteStringToFile(size, sys_ra.c_str());
+        LINFO << "Set readahead_kb: " << size << " on " << sys_ra;
+
+        auto parent = dm.GetParentBlockDeviceByPath(block_device);
+        if (!parent) {
+            return;
+        }
+        block_device = *parent;
+    }
+}
+
 //
 // Prepare the filesystem on the given block device to be mounted.
 //
@@ -667,6 +707,11 @@
     }
     mkdir(mount_point.c_str(), 0755);
 
+    // Don't need to return error, since it's a salt
+    if (entry.readahead_size_kb != -1) {
+        SetReadAheadSize(blk_device, entry.readahead_size_kb);
+    }
+
     int fs_stat = 0;
 
     if (is_extfs(entry.fs_type)) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0c0862e..42bf356 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
+#include <fnmatch.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -254,6 +255,13 @@
             } else {
                 entry->reserved_size = static_cast<off64_t>(size);
             }
+        } else if (StartsWith(flag, "readahead_size_kb=")) {
+            int val;
+            if (ParseInt(arg, &val, 0, 16 * 1024)) {
+                entry->readahead_size_kb = val;
+            } else {
+                LWARNING << "Warning: readahead_size_kb= flag malformed (0 ~ 16MB): " << arg;
+            }
         } else if (StartsWith(flag, "eraseblk=")) {
             // The erase block size flag is followed by an = and the flash erase block size. Get it,
             // check that it is a power of 2 and at least 4096, and return it.
@@ -680,7 +688,7 @@
     }
 }
 
-bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (!fstab_file) {
         PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
@@ -689,41 +697,51 @@
 
     bool is_proc_mounts = path == "/proc/mounts";
 
-    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, fstab)) {
+    Fstab fstab;
+    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
-    if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
-        // This is expected to fail if host is android Q, since Q doesn't
-        // support DSU slotting. The DSU "active" indicator file would be
-        // non-existent or empty if DSU is enabled within the guest system.
-        // In that case, just use the default slot name "dsu".
-        std::string dsu_slot;
-        if (!android::gsi::GetActiveDsu(&dsu_slot)) {
-            PWARNING << __FUNCTION__ << "(): failed to get active dsu slot";
+    if (!is_proc_mounts) {
+        if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+            // This is expected to fail if host is android Q, since Q doesn't
+            // support DSU slotting. The DSU "active" indicator file would be
+            // non-existent or empty if DSU is enabled within the guest system.
+            // In that case, just use the default slot name "dsu".
+            std::string dsu_slot;
+            if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
+                PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
+                return false;
+            }
+            if (dsu_slot.empty()) {
+                dsu_slot = "dsu";
+                LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
+            }
+            // This file is non-existent on Q vendor.
+            std::string lp_names;
+            if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
+                PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
+                return false;
+            }
+            TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
+        } else if (errno != ENOENT) {
+            PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
+            return false;
         }
-        if (dsu_slot.empty()) {
-            dsu_slot = "dsu";
-        }
-
-        std::string lp_names;
-        ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
-        TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, ","));
     }
 
-#ifndef NO_SKIP_MOUNT
-    SkipMountingPartitions(fstab);
-#endif
-    EnableMandatoryFlags(fstab);
+    SkipMountingPartitions(&fstab, false /* verbose */);
+    EnableMandatoryFlags(&fstab);
 
+    *fstab_out = std::move(fstab);
     return true;
 }
 
 // Returns fstab entries parsed from the device tree if they exist
-bool ReadFstabFromDt(Fstab* fstab, bool log) {
+bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
     std::string fstab_buf = ReadFstabFromDt();
     if (fstab_buf.empty()) {
-        if (log) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
+        if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
         return false;
     }
 
@@ -731,34 +749,36 @@
         fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
                  fstab_buf.length(), "r"), fclose);
     if (!fstab_file) {
-        if (log) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+        if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
         return false;
     }
 
     if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
-        if (log) {
+        if (verbose) {
             LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
                    << fstab_buf;
         }
         return false;
     }
 
-#ifndef NO_SKIP_MOUNT
-    SkipMountingPartitions(fstab);
-#endif
+    SkipMountingPartitions(fstab, verbose);
 
     return true;
 }
 
-#ifndef NO_SKIP_MOUNT
+#ifdef NO_SKIP_MOUNT
+bool SkipMountingPartitions(Fstab*, bool) {
+    return true;
+}
+#else
 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
 // only common files for all targets can be put into system partition. It is under
 // /system/system_ext because GSI is a single system.img that includes the contents of system_ext
 // partition and product partition under /system/system_ext and /system/product, respectively.
-bool SkipMountingPartitions(Fstab* fstab) {
-    constexpr const char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
+bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
+    static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
 
     std::string skip_config;
     auto save_errno = errno;
@@ -767,29 +787,39 @@
         return true;
     }
 
-    for (const auto& skip_mount_point : Split(skip_config, "\n")) {
-        if (skip_mount_point.empty()) {
+    std::vector<std::string> skip_mount_patterns;
+    for (const auto& line : Split(skip_config, "\n")) {
+        if (line.empty() || StartsWith(line, "#")) {
             continue;
         }
-        auto it = std::remove_if(fstab->begin(), fstab->end(),
-                                 [&skip_mount_point](const auto& entry) {
-                                     return entry.mount_point == skip_mount_point;
-                                 });
-        if (it == fstab->end()) continue;
-        fstab->erase(it, fstab->end());
-        LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
+        skip_mount_patterns.push_back(line);
     }
 
+    // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry
+    // would be partitioned to the second group.
+    auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool {
+        for (const auto& pattern : skip_mount_patterns) {
+            if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) {
+                return false;
+            }
+        }
+        return true;
+    };
+    auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch);
+    if (verbose) {
+        for (auto it = remove_from; it != fstab->end(); ++it) {
+            LINFO << "Skip mounting mountpoint: " << it->mount_point;
+        }
+    }
+    fstab->erase(remove_from, fstab->end());
     return true;
 }
 #endif
 
 // Loads the fstab file and combines with fstab entries passed in from device tree.
 bool ReadDefaultFstab(Fstab* fstab) {
-    Fstab dt_fstab;
-    ReadFstabFromDt(&dt_fstab, false);
-
-    *fstab = std::move(dt_fstab);
+    fstab->clear();
+    ReadFstabFromDt(fstab, false /* verbose */);
 
     std::string default_fstab_path;
     // Use different fstab paths for normal boot and recovery boot, respectively
@@ -800,16 +830,14 @@
     }
 
     Fstab default_fstab;
-    if (!default_fstab_path.empty()) {
-        ReadFstabFromFile(default_fstab_path, &default_fstab);
+    if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
+        for (auto&& entry : default_fstab) {
+            fstab->emplace_back(std::move(entry));
+        }
     } else {
         LINFO << __FUNCTION__ << "(): failed to find device default fstab";
     }
 
-    for (auto&& entry : default_fstab) {
-        fstab->emplace_back(std::move(entry));
-    }
-
     return !fstab->empty();
 }
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 2d4de09..2704e47 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -47,6 +47,7 @@
     int max_comp_streams = 0;
     off64_t zram_size = 0;
     off64_t reserved_size = 0;
+    off64_t readahead_size_kb = -1;
     std::string encryption_options;
     off64_t erase_blk_size = 0;
     off64_t logical_blk_size = 0;
@@ -97,9 +98,9 @@
 using Fstab = std::vector<FstabEntry>;
 
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
-bool ReadFstabFromDt(Fstab* fstab, bool log = true);
+bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
 bool ReadDefaultFstab(Fstab* fstab);
-bool SkipMountingPartitions(Fstab* fstab);
+bool SkipMountingPartitions(Fstab* fstab, bool verbose = false);
 
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 // The Fstab can contain multiple entries for the same mount point with different configurations.
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 50f4f33..7e30509 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -198,7 +198,7 @@
 
     ~MappedDevice();
 
-    int fd() const { return fd_; }
+    int fd() const { return fd_.get(); }
     const std::string& path() const { return path_; }
 
   protected:
diff --git a/fs_mgr/libfs_avb/TEST_MAPPING b/fs_mgr/libfs_avb/TEST_MAPPING
deleted file mode 100644
index b0f36d4..0000000
--- a/fs_mgr/libfs_avb/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "postsubmit": [
-    {
-      "name": "libfs_avb_test",
-      "host": true
-    },
-    {
-      "name": "libfs_avb_internal_test",
-      "host": true
-    }
-  ]
-}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index ea92d25..3cb4123 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -264,6 +264,7 @@
         "android.hardware.boot@1.0",
         "android.hardware.boot@1.1",
         "libbrotli",
+        "libc++fs",
         "libfs_mgr",
         "libgsi",
         "libgmock",
@@ -297,6 +298,7 @@
     ],
     static_libs: [
         "libbrotli",
+        "libc++fs",
         "libfstab",
         "libsnapshot",
         "libsnapshot_cow",
@@ -326,6 +328,7 @@
         "power_test.cpp",
     ],
     static_libs: [
+        "libc++fs",
         "libsnapshot",
         "update_metadata-protos",
     ],
@@ -355,6 +358,7 @@
     static_libs: [
         "libbase",
         "libbrotli",
+        "libc++fs",
         "libchrome",
         "libcrypto_static",
         "libcutils",
@@ -416,7 +420,7 @@
         "snapuserd_server.cpp",
         "snapuserd.cpp",
         "snapuserd_daemon.cpp",
-	"snapuserd_worker.cpp",
+        "snapuserd_worker.cpp",
     ],
 
     cflags: [
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
new file mode 100644
index 0000000..ac2c787
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2021 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 <memory>
+
+#include <gmock/gmock.h>
+#include <libsnapshot/snapshot_stats.h>
+
+namespace android::snapshot {
+
+class MockSnapshotMergeStats final : public ISnapshotMergeStats {
+  public:
+    virtual ~MockSnapshotMergeStats() = default;
+    // Called when merge starts or resumes.
+    MOCK_METHOD(bool, Start, (), (override));
+    MOCK_METHOD(void, set_state, (android::snapshot::UpdateState, bool), (override));
+    MOCK_METHOD(void, set_cow_file_size, (uint64_t), ());
+    MOCK_METHOD(void, set_total_cow_size_bytes, (uint64_t), (override));
+    MOCK_METHOD(void, set_estimated_cow_size_bytes, (uint64_t), (override));
+    MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
+    MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
+    MOCK_METHOD(uint64_t, cow_file_size, (), (override));
+    MOCK_METHOD(uint64_t, total_cow_size_bytes, (), (override));
+    MOCK_METHOD(uint64_t, estimated_cow_size_bytes, (), (override));
+    MOCK_METHOD(uint32_t, boot_complete_time_ms, (), (override));
+    MOCK_METHOD(uint32_t, boot_complete_to_merge_start_time_ms, (), (override));
+    MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
+
+    using ISnapshotMergeStats::Result;
+    // Return nullptr if any failure.
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 7e74fac..126e1a0 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -493,6 +493,9 @@
     // Unmap a COW image device previously mapped with MapCowImage().
     bool UnmapCowImage(const std::string& name);
 
+    // Unmap a COW and remove it from a MetadataBuilder.
+    void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata);
+
     // Unmap and remove all known snapshots.
     bool RemoveAllSnapshots(LockedFile* lock);
 
@@ -738,6 +741,10 @@
     // Helper of UpdateUsesCompression
     bool UpdateUsesCompression(LockedFile* lock);
 
+    // Wrapper around libdm, with diagnostics.
+    bool DeleteDeviceIfExists(const std::string& name,
+                              const std::chrono::milliseconds& timeout_ms = {});
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index 1dab361..280e857 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -69,6 +69,8 @@
     // must ONLY be called if the control device has already been deleted.
     bool WaitForDeviceDelete(const std::string& control_device);
 
+    void CloseConnection() { sockfd_ = {}; }
+
     // Detach snapuserd. This shuts down the listener socket, and will cause
     // snapuserd to gracefully exit once all handler threads have terminated.
     // This should only be used on first-stage instances of snapuserd.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index bd1e284..c504355 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <sys/unistd.h>
 
+#include <filesystem>
 #include <optional>
 #include <thread>
 #include <unordered_set>
@@ -587,8 +588,7 @@
 bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
     CHECK(lock);
 
-    auto& dm = DeviceMapper::Instance();
-    if (!dm.DeleteDeviceIfExists(name)) {
+    if (!DeleteDeviceIfExists(name)) {
         LOG(ERROR) << "Could not delete snapshot device: " << name;
         return false;
     }
@@ -1252,25 +1252,6 @@
     return true;
 }
 
-static bool DeleteDmDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
-    auto start = std::chrono::steady_clock::now();
-    auto& dm = DeviceMapper::Instance();
-    while (true) {
-        if (dm.DeleteDeviceIfExists(name)) {
-            break;
-        }
-        auto now = std::chrono::steady_clock::now();
-        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
-        if (elapsed >= timeout_ms) {
-            LOG(ERROR) << "DeleteDevice timeout: " << name;
-            return false;
-        }
-        std::this_thread::sleep_for(400ms);
-    }
-
-    return true;
-}
-
 bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
                                              const SnapshotStatus& status) {
     auto& dm = DeviceMapper::Instance();
@@ -1326,11 +1307,11 @@
         UnmapDmUserDevice(name);
     }
     auto base_name = GetBaseDeviceName(name);
-    if (!dm.DeleteDeviceIfExists(base_name)) {
+    if (!DeleteDeviceIfExists(base_name)) {
         LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
     }
 
-    if (!DeleteDmDevice(GetSourceDeviceName(name), 4000ms)) {
+    if (!DeleteDeviceIfExists(GetSourceDeviceName(name), 4000ms)) {
         LOG(ERROR) << "Unable to delete source device for snapshot: " << GetSourceDeviceName(name);
     }
 
@@ -1619,6 +1600,18 @@
         //    as dm-snapshot (for example, after merge completes).
         bool should_unmap = current_slot != Slot::Target;
         bool should_delete = ShouldDeleteSnapshot(flashing_status, current_slot, name);
+        if (should_unmap && android::base::EndsWith(name, device_->GetSlotSuffix())) {
+            // Something very unexpected has happened - we want to unmap this
+            // snapshot, but it's on the wrong slot. We can't unmap an active
+            // partition. If this is not really a snapshot, skip the unmap
+            // step.
+            auto& dm = DeviceMapper::Instance();
+            if (dm.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
+                LOG(ERROR) << "Detected snapshot " << name << " on " << current_slot << " slot"
+                           << " for source partition; removing without unmap.";
+                should_unmap = false;
+            }
+        }
 
         bool partition_ok = true;
         if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
@@ -2071,15 +2064,14 @@
         return false;
     }
 
-    auto& dm = DeviceMapper::Instance();
     auto base_name = GetBaseDeviceName(target_partition_name);
-    if (!dm.DeleteDeviceIfExists(base_name)) {
+    if (!DeleteDeviceIfExists(base_name)) {
         LOG(ERROR) << "Cannot delete base device: " << base_name;
         return false;
     }
 
     auto source_name = GetSourceDeviceName(target_partition_name);
-    if (!dm.DeleteDeviceIfExists(source_name)) {
+    if (!DeleteDeviceIfExists(source_name)) {
         LOG(ERROR) << "Cannot delete source device: " << source_name;
         return false;
     }
@@ -2169,7 +2161,7 @@
         return false;
     }
 
-    if (!DeleteDmDevice(GetCowName(name), 4000ms)) {
+    if (!DeleteDeviceIfExists(GetCowName(name), 4000ms)) {
         LOG(ERROR) << "Cannot unmap: " << GetCowName(name);
         return false;
     }
@@ -2190,7 +2182,7 @@
         return true;
     }
 
-    if (!dm.DeleteDeviceIfExists(dm_user_name)) {
+    if (!DeleteDeviceIfExists(dm_user_name)) {
         LOG(ERROR) << "Cannot unmap " << dm_user_name;
         return false;
     }
@@ -2285,6 +2277,17 @@
             return false;
         }
     }
+
+    // Terminate the daemon and release the snapuserd_client_ object.
+    // If we need to re-connect with the daemon, EnsureSnapuserdConnected()
+    // will re-create the object and establish the socket connection.
+    if (snapuserd_client_) {
+        LOG(INFO) << "Shutdown snapuserd daemon";
+        snapuserd_client_->DetachSnapuserd();
+        snapuserd_client_->CloseConnection();
+        snapuserd_client_ = nullptr;
+    }
+
     return true;
 }
 
@@ -2570,11 +2573,10 @@
     return true;
 }
 
-static void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
-    auto& dm = DeviceMapper::Instance();
+void SnapshotManager::UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
     std::vector<std::string> to_delete;
     for (auto* existing_cow_partition : current_metadata->ListPartitionsInGroup(kCowGroupName)) {
-        if (!dm.DeleteDeviceIfExists(existing_cow_partition->name())) {
+        if (!DeleteDeviceIfExists(existing_cow_partition->name())) {
             LOG(WARNING) << existing_cow_partition->name()
                          << " cannot be unmapped and its space cannot be reclaimed";
             continue;
@@ -3603,5 +3605,71 @@
     stats->set_estimated_cow_size_bytes(estimated_cow_size);
 }
 
+bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
+                                           const std::chrono::milliseconds& timeout_ms) {
+    auto& dm = DeviceMapper::Instance();
+    auto start = std::chrono::steady_clock::now();
+    while (true) {
+        if (dm.DeleteDeviceIfExists(name)) {
+            return true;
+        }
+        auto now = std::chrono::steady_clock::now();
+        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
+        if (elapsed >= timeout_ms) {
+            break;
+        }
+        std::this_thread::sleep_for(400ms);
+    }
+
+    // Try to diagnose why this failed. First get the actual device path.
+    std::string full_path;
+    if (!dm.GetDmDevicePathByName(name, &full_path)) {
+        LOG(ERROR) << "Unable to diagnose DM_DEV_REMOVE failure.";
+        return false;
+    }
+
+    // Check for child dm-devices.
+    std::string block_name = android::base::Basename(full_path);
+    std::string sysfs_holders = "/sys/class/block/" + block_name + "/holders";
+
+    std::error_code ec;
+    std::filesystem::directory_iterator dir_iter(sysfs_holders, ec);
+    if (auto begin = std::filesystem::begin(dir_iter); begin != std::filesystem::end(dir_iter)) {
+        LOG(ERROR) << "Child device-mapper device still mapped: " << begin->path();
+        return false;
+    }
+
+    // Check for mounted partitions.
+    android::fs_mgr::Fstab fstab;
+    android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab);
+    for (const auto& entry : fstab) {
+        if (android::base::Basename(entry.blk_device) == block_name) {
+            LOG(ERROR) << "Partition still mounted: " << entry.mount_point;
+            return false;
+        }
+    }
+
+    // Check for detached mounted partitions.
+    for (const auto& fs : std::filesystem::directory_iterator("/sys/fs", ec)) {
+        std::string fs_type = android::base::Basename(fs.path().c_str());
+        if (!(fs_type == "ext4" || fs_type == "f2fs")) {
+            continue;
+        }
+
+        std::string path = fs.path().c_str() + "/"s + block_name;
+        if (access(path.c_str(), F_OK) == 0) {
+            LOG(ERROR) << "Block device was lazily unmounted and is still in-use: " << full_path
+                       << "; possibly open file descriptor or attached loop device.";
+            return false;
+        }
+    }
+
+    LOG(ERROR) << "Device-mapper device " << name << "(" << full_path << ")"
+               << " still in use."
+               << "  Probably a file descriptor was leaked or held open, or a loop device is"
+               << " attached.";
+    return false;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 25500b5..8fae00b 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2019,6 +2019,36 @@
 
     // Read bytes back and verify they match the cache.
     ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+
+    ASSERT_TRUE(sm->UnmapAllSnapshots());
+}
+
+TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) {
+    AddOperationForPartitions();
+
+    // Execute the update from B->A.
+    test_device->set_slot_suffix("_b");
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    std::string path;
+    ASSERT_TRUE(CreateLogicalPartition(
+            CreateLogicalPartitionParams{
+                    .block_device = fake_super,
+                    .metadata_slot = 0,
+                    .partition_name = "sys_a",
+                    .timeout_ms = 1s,
+                    .partition_opener = opener_.get(),
+            },
+            &path));
+
+    // Hold sys_a open so it can't be unmapped.
+    unique_fd fd(open(path.c_str(), O_RDONLY));
+
+    // Switch back to "A", make sure we can cancel. Instead of unmapping sys_a
+    // we should simply delete the old snapshots.
+    test_device->set_slot_suffix("_a");
+    ASSERT_TRUE(sm->BeginUpdate());
 }
 
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index a44de84..5eb2003 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -48,6 +48,17 @@
     return SnapshotManager::New()->Dump(std::cout);
 }
 
+bool MapCmdHandler(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    using namespace std::chrono_literals;
+    return SnapshotManager::New()->MapAllSnapshots(5000ms);
+}
+
+bool UnmapCmdHandler(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    return SnapshotManager::New()->UnmapAllSnapshots();
+}
+
 bool MergeCmdHandler(int /*argc*/, char** argv) {
     android::base::InitLogging(argv, &android::base::StderrLogger);
     LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
@@ -58,6 +69,8 @@
         // clang-format off
         {"dump", DumpCmdHandler},
         {"merge", MergeCmdHandler},
+        {"map", MapCmdHandler},
+        {"unmap", UnmapCmdHandler},
         // clang-format on
 };
 
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
index 9335364..87c5528 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -158,6 +158,7 @@
     bool CommitMerge(int num_merge_ops);
 
     void CloseFds() { cow_fd_ = {}; }
+    void FreeResources() { worker_threads_.clear(); }
     size_t GetMetadataAreaSize() { return vec_.size(); }
     void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
 
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 167895e..64332d1 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -219,7 +219,13 @@
         auto iter = FindHandler(&lock, handler->misc_name());
         if (iter == dm_users_.end()) {
             // RemoveAndJoinHandler() already removed us from the list, and is
-            // now waiting on a join(), so just return.
+            // now waiting on a join(), so just return. Additionally, release
+            // all the resources held by snapuserd object which are shared
+            // by worker threads. This should be done when the last reference
+            // of "handler" is released; but we will explicitly release here
+            // to make sure snapuserd object is freed as it is the biggest
+            // consumer of memory in the daemon.
+            handler->FreeResources();
             LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
             return;
         }
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h
index e9d575d..6699189 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd_server.h
@@ -49,7 +49,15 @@
   public:
     explicit DmUserHandler(std::shared_ptr<Snapuserd> snapuserd);
 
-    void FreeResources() { snapuserd_ = nullptr; }
+    void FreeResources() {
+        // Each worker thread holds a reference to snapuserd.
+        // Clear them so that all the resources
+        // held by snapuserd is released
+        if (snapuserd_) {
+            snapuserd_->FreeResources();
+            snapuserd_ = nullptr;
+        }
+    }
     const std::shared_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
     std::thread& thread() { return thread_; }
 
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 5887641..9adb6bd 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1097,3 +1097,59 @@
     ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
             << "/data wasn't mounted from default fstab";
 }
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      readahead_size_kb=blah
+source none1       swap   defaults      readahead_size_kb=128
+source none2       swap   defaults      readahead_size_kb=5%
+source none3       swap   defaults      readahead_size_kb=5kb
+source none4       swap   defaults      readahead_size_kb=16385
+source none5       swap   defaults      readahead_size_kb=-128
+source none6       swap   defaults      readahead_size_kb=0
+)fs";
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_EQ(7U, fstab.size());
+
+    FstabEntry::FsMgrFlags flags = {};
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none1", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(128, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none2", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none3", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none4", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none5", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(-1, entry->readahead_size_kb);
+    entry++;
+
+    EXPECT_EQ("none6", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->readahead_size_kb);
+}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index a11bb28..3faf430 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -97,7 +97,6 @@
 
     bool MountPartitions();
     bool TrySwitchSystemAsRoot();
-    bool TrySkipMountingPartitions();
     bool IsDmLinearEnabled();
     void GetSuperDeviceName(std::set<std::string>* devices);
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
@@ -534,7 +533,7 @@
 bool FirstStageMount::MountPartitions() {
     if (!TrySwitchSystemAsRoot()) return false;
 
-    if (!SkipMountingPartitions(&fstab_)) return false;
+    if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
 
     for (auto current = fstab_.begin(); current != fstab_.end();) {
         // We've already mounted /system above.
diff --git a/init/init.cpp b/init/init.cpp
index 7264b22..a7325ca 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -849,21 +849,6 @@
     auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
     SetProperty(gsi::kGsiInstalledProp, is_installed);
 
-    /*
-     * For debug builds of S launching devices, init mounts debugfs for
-     * enabling vendor debug data collection setup at boot time. Init will unmount it on
-     * boot-complete after vendor code has performed the required initializations
-     * during boot. Dumpstate will then mount debugfs in order to read data
-     * from the same using the dumpstate HAL during bugreport creation.
-     * Dumpstate will also unmount debugfs after bugreport creation.
-     * first_api_level comparison is done here instead
-     * of init.rc since init.rc parser does not support >/< operators.
-     */
-    auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
-    bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
-    auto mount_debugfs = (is_debuggable && (api_level >= 31)) ? "1" : "0";
-    SetProperty("init.mount_debugfs", mount_debugfs);
-
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 382f430..c2eb73c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1165,15 +1165,57 @@
 
 constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
 
+// emulator specific, should be removed once emulator is migrated to
+// bootconfig, see b/182291166.
+static std::string RemapEmulatorPropertyName(const std::string_view qemu_key) {
+    if (StartsWith(qemu_key, "dalvik."sv) || StartsWith(qemu_key, "opengles."sv) ||
+        StartsWith(qemu_key, "config."sv)) {
+        return std::string(qemu_key);
+    } else if (qemu_key == "uirenderer"sv) {
+        return "debug.hwui.renderer"s;
+    } else if (qemu_key == "media.ccodec"sv) {
+        return "debug.stagefright.ccodec"s;
+    } else {
+        return "qemu."s + std::string(qemu_key);
+    }
+}
+
 static void ProcessKernelCmdline() {
     ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+        constexpr auto qemu_prefix = "qemu."sv;
+
         if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
             InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
-        } else if (StartsWith(key, "qemu."sv)) {
-            InitPropertySet("ro.kernel." + key, value);
-        } else if (key == "qemu") {
+        } else if (StartsWith(key, qemu_prefix)) {
             InitPropertySet("ro.kernel." + key, value);  // emulator specific, deprecated
+
+            // emulator specific, should be retired once emulator migrates to
+            // androidboot.
+            const auto new_name =
+                    RemapEmulatorPropertyName(std::string_view(key).substr(qemu_prefix.size()));
+            if (!new_name.empty()) {
+                InitPropertySet("ro.boot." + new_name, value);
+            }
+        } else if (key == "qemu") {
+            // emulator specific, should be retired once emulator migrates to
+            // androidboot.
             InitPropertySet("ro.boot." + key, value);
+        } else if (key == "android.bootanim" && value == "0") {
+            // emulator specific, should be retired once emulator migrates to
+            // androidboot.
+            InitPropertySet("ro.boot.debug.sf.nobootanimation", "1");
+        } else if (key == "android.checkjni") {
+            // emulator specific, should be retired once emulator migrates to
+            // androidboot.
+            std::string value_bool;
+            if (value == "0") {
+                value_bool = "false";
+            } else if (value == "1") {
+                value_bool = "true";
+            } else {
+                value_bool = value;
+            }
+            InitPropertySet("ro.boot.dalvik.vm.checkjni", value_bool);
         }
     });
 }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index b417396..35a96f9 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -667,7 +667,7 @@
         extra_fstab.emplace_back(std::move(entry));
     }
 
-    SkipMountingPartitions(&extra_fstab);
+    SkipMountingPartitions(&extra_fstab, true /* verbose */);
     if (extra_fstab.empty()) {
         return;
     }
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index cab988b..2221228 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -106,10 +106,10 @@
     }
 
     if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
-                     [&args](const auto& other) { return other.devpath == args[2]; }) !=
+                     [&args](const auto& other) { return other.devpath == args[1]; }) !=
         external_firmware_handlers->end()) {
         return Error() << "found a previous external_firmware_handler with the same devpath, '"
-                       << args[2] << "'";
+                       << args[1] << "'";
     }
 
     passwd* pwd = getpwnam(args[2].c_str());
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index b604c53..4e63ba5 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -45,6 +45,13 @@
     EXPECT_EQ(expected.attribute_, test.attribute_);
 }
 
+void TestExternalFirmwareHandler(const ExternalFirmwareHandler& expected,
+                                 const ExternalFirmwareHandler& test) {
+    EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
+    EXPECT_EQ(expected.uid, test.uid) << expected.uid;
+    EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
+}
+
 template <typename T, typename F>
 void TestVector(const T& expected, const T& test, F function) {
     ASSERT_EQ(expected.size(), test.size());
@@ -67,6 +74,8 @@
     TestVector(expected.sysfs_permissions, result.sysfs_permissions, TestSysfsPermissions);
     TestVector(expected.dev_permissions, result.dev_permissions, TestPermissions);
     EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
+    TestVector(expected.external_firmware_handlers, result.external_firmware_handlers,
+               TestExternalFirmwareHandler);
 }
 
 TEST(ueventd_parser, EmptyFile) {
@@ -144,7 +153,7 @@
     auto ueventd_file = R"(
 external_firmware_handler devpath root handler_path
 external_firmware_handler /devices/path/firmware/something001.bin system /vendor/bin/firmware_handler.sh
-external_firmware_handler /devices/path/firmware/something001.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
+external_firmware_handler /devices/path/firmware/something002.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
 )";
 
     auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
@@ -159,7 +168,7 @@
                     "/vendor/bin/firmware_handler.sh",
             },
             {
-                    "/devices/path/firmware/something001.bin",
+                    "/devices/path/firmware/something002.bin",
                     AID_RADIO,
                     "/vendor/bin/firmware_handler.sh --has --arguments",
             },
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 0d9f2c7..0f3763c 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -354,3 +354,17 @@
     defaults: ["libcutils_test_static_defaults"],
     test_config: "KernelLibcutilsTest.xml",
 }
+
+rust_bindgen {
+    name: "libcutils_bindgen",
+    wrapper_src: "rust/cutils.h",
+    crate_name: "cutils_bindgen",
+    source_stem: "bindings",
+    local_include_dirs: ["include"],
+    bindgen_flags: [
+        "--whitelist-function", "multiuser_get_app_id",
+        "--whitelist-function", "multiuser_get_user_id",
+        "--whitelist-function", "multiuser_get_uid",
+        "--whitelist-var", "AID_USER_OFFSET",
+    ],
+}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index d69c038..e9497a8 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -86,7 +86,7 @@
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/bin" },
     { 00755, AID_ROOT,         AID_ROOT,         0, "system/etc/ppp" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
+    { 00750, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/apex/*/bin" },
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index b4fe2e6..7489281 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -127,6 +127,7 @@
 #define AID_EXT_DATA_RW 1078      /* GID for app-private data directories on external storage */
 #define AID_EXT_OBB_RW 1079       /* GID for OBB directories on external storage */
 #define AID_CONTEXT_HUB 1080      /* GID for access to the Context Hub */
+#define AID_VIRTMANAGER 1081      /* VirtManager daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/rust/cutils.h b/libcutils/rust/cutils.h
new file mode 100644
index 0000000..9b78af6
--- /dev/null
+++ b/libcutils/rust/cutils.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
diff --git a/libmodprobe/OWNERS b/libmodprobe/OWNERS
index e6b5bba..a6796cb 100644
--- a/libmodprobe/OWNERS
+++ b/libmodprobe/OWNERS
@@ -1 +1,2 @@
-smuckle@google.com
+dvander@google.com
+willmcvicker@google.com
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index baee4f9..c934860 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -24,7 +24,8 @@
 
 class Modprobe {
   public:
-    Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load");
+    Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
+             bool use_blocklist = true);
 
     bool LoadListedModules(bool strict = true);
     bool LoadWithAliases(const std::string& module_name, bool strict,
@@ -36,7 +37,6 @@
                             std::vector<std::string>* post_dependencies);
     void ResetModuleCount() { module_count_ = 0; }
     int GetModuleCount() { return module_count_; }
-    void EnableBlocklist(bool enable);
 
   private:
     std::string MakeCanonical(const std::string& module_path);
@@ -48,6 +48,7 @@
     void AddOption(const std::string& module_name, const std::string& option_name,
                    const std::string& value);
     std::string GetKernelCmdline();
+    bool IsBlocklisted(const std::string& module_name);
 
     bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
     bool ParseAliasCallback(const std::vector<std::string>& args);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index b3ae937..1a9d364 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -313,7 +313,9 @@
     }
 }
 
-Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file) {
+Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file,
+                   bool use_blocklist)
+    : blocklist_enabled(use_blocklist) {
     using namespace std::placeholders;
 
     for (const auto& base_path : base_paths) {
@@ -339,10 +341,6 @@
     ParseKernelCmdlineOptions();
 }
 
-void Modprobe::EnableBlocklist(bool enable) {
-    blocklist_enabled = enable;
-}
-
 std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
     auto it = module_deps_.find(module);
     if (it == module_deps_.end()) {
@@ -427,10 +425,23 @@
     return true;
 }
 
+bool Modprobe::IsBlocklisted(const std::string& module_name) {
+    if (!blocklist_enabled) return false;
+
+    auto canonical_name = MakeCanonical(module_name);
+    auto dependencies = GetDependencies(canonical_name);
+    for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
+        if (module_blocklist_.count(MakeCanonical(*dep))) return true;
+    }
+
+    return module_blocklist_.count(canonical_name) > 0;
+}
+
 bool Modprobe::LoadListedModules(bool strict) {
     auto ret = true;
     for (const auto& module : module_load_) {
         if (!LoadWithAliases(module, true)) {
+            if (IsBlocklisted(module)) continue;
             ret = false;
             if (strict) break;
         }
@@ -440,16 +451,10 @@
 
 bool Modprobe::Remove(const std::string& module_name) {
     auto dependencies = GetDependencies(MakeCanonical(module_name));
-    if (dependencies.empty()) {
-        LOG(ERROR) << "Empty dependencies for module " << module_name;
-        return false;
-    }
-    if (!Rmmod(dependencies[0])) {
-        return false;
-    }
-    for (auto dep = dependencies.begin() + 1; dep != dependencies.end(); ++dep) {
+    for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
         Rmmod(*dep);
     }
+    Rmmod(module_name);
     return true;
 }
 
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index d50c10d..f960b61 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -78,6 +78,18 @@
             "/test13.ko",
     };
 
+    std::vector<std::string> expected_modules_blocklist_enabled = {
+            "/test1.ko option1=50 option2=60",
+            "/test6.ko",
+            "/test2.ko",
+            "/test5.ko option1=",
+            "/test8.ko",
+            "/test7.ko param1=4",
+            "/test12.ko",
+            "/test11.ko",
+            "/test13.ko",
+    };
+
     const std::string modules_dep =
             "test1.ko:\n"
             "test2.ko:\n"
@@ -146,7 +158,7 @@
         *i = dir.path + *i;
     }
 
-    Modprobe m({dir.path});
+    Modprobe m({dir.path}, "modules.load", false);
     EXPECT_TRUE(m.LoadListedModules());
 
     GTEST_LOG_(INFO) << "Expected modules loaded (in order):";
@@ -176,8 +188,22 @@
 
     EXPECT_TRUE(modules_loaded == expected_after_remove);
 
-    m.EnableBlocklist(true);
+    m = Modprobe({dir.path});
     EXPECT_FALSE(m.LoadWithAliases("test4", true));
+    while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front()));
+    EXPECT_TRUE(m.LoadListedModules());
+
+    GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):";
+    for (auto i = expected_modules_blocklist_enabled.begin();
+         i != expected_modules_blocklist_enabled.end(); ++i) {
+        *i = dir.path + *i;
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+    GTEST_LOG_(INFO) << "Actual modules loaded with blocklist enabled (in order):";
+    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+    EXPECT_TRUE(modules_loaded == expected_modules_blocklist_enabled);
 }
 
 TEST(libmodprobe, ModuleDepLineWithoutColonIsSkipped) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 5b57bdd..bd94621 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -70,11 +70,11 @@
       "Name": "Frozen",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "freezer",
-            "Path": ""
+            "Name": "FreezerState",
+            "Value": "1"
           }
         }
       ]
@@ -83,11 +83,11 @@
       "Name": "Unfrozen",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "freezer",
-            "Path": "../"
+            "Name": "FreezerState",
+            "Value": "0"
           }
         }
       ]
@@ -558,32 +558,6 @@
         }
       ]
     },
-    {
-      "Name": "FreezerDisabled",
-      "Actions": [
-        {
-          "Name": "SetAttribute",
-          "Params":
-          {
-            "Name": "FreezerState",
-            "Value": "0"
-          }
-        }
-      ]
-    },
-    {
-      "Name": "FreezerEnabled",
-      "Actions": [
-        {
-          "Name": "SetAttribute",
-          "Params":
-          {
-            "Name": "FreezerState",
-            "Value": "1"
-          }
-        }
-      ]
-    }
   ],
 
   "AggregateProfiles": [
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index f13a681..db00a49 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -518,10 +518,10 @@
                 std::string attr_filepath = params_val["FilePath"].asString();
                 std::string attr_value = params_val["Value"].asString();
                 if (!attr_filepath.empty() && !attr_value.empty()) {
-                    const Json::Value& logfailures = params_val["LogFailures"];
-                    bool attr_logfailures = logfailures.isNull() || logfailures.asBool();
+                    std::string attr_logfailures = params_val["LogFailures"].asString();
+                    bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
                     profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value,
-                                                                   attr_logfailures));
+                                                                   logfailures));
                 } else if (attr_filepath.empty()) {
                     LOG(WARNING) << "WriteFile: invalid parameter: "
                                  << "empty filepath";
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
new file mode 100644
index 0000000..354c7b3
--- /dev/null
+++ b/libstats/pull_rust/Android.bp
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_bindgen {
+    name: "libstatspull_bindgen",
+    wrapper_src: "statslog.h",
+    crate_name: "statspull_bindgen",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--whitelist-function=AStatsEventList_addStatsEvent",
+        "--whitelist-function=AStatsEvent_.*",
+        "--whitelist-function=AStatsManager_.*",
+        "--whitelist-var=AStatsManager_.*",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "libstatspull",
+                "libstatssocket",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libstatspull",
+                "libstatssocket",
+            ],
+        },
+    },
+}
+
+rust_library {
+    name: "libstatspull_rust",
+    crate_name: "statspull_rust",
+    srcs: ["stats_pull.rs"],
+    rustlibs: [
+        "liblazy_static",
+        "liblog_rust",
+        "libstatslog_rust_header",
+        "libstatspull_bindgen",
+    ],
+}
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
new file mode 100644
index 0000000..174125e
--- /dev/null
+++ b/libstats/pull_rust/stats_pull.rs
@@ -0,0 +1,170 @@
+// Copyright 2021, 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.
+
+//! A Rust interface for the StatsD pull API.
+
+use lazy_static::lazy_static;
+use statslog_rust_header::{Atoms, Stat, StatsError};
+use statspull_bindgen::*;
+use std::collections::HashMap;
+use std::convert::TryInto;
+use std::os::raw::c_void;
+use std::sync::Mutex;
+
+/// The return value of callbacks.
+pub type StatsPullResult = Vec<Box<dyn Stat>>;
+
+/// A wrapper for AStatsManager_PullAtomMetadata.
+/// It calls AStatsManager_PullAtomMetadata_release on drop.
+pub struct Metadata {
+    metadata: *mut AStatsManager_PullAtomMetadata,
+}
+
+impl Metadata {
+    /// Calls AStatsManager_PullAtomMetadata_obtain.
+    pub fn new() -> Self {
+        // Safety: We panic if the memory allocation fails.
+        let metadata = unsafe { AStatsManager_PullAtomMetadata_obtain() };
+        if metadata.is_null() {
+            panic!("Cannot obtain pull atom metadata.");
+        } else {
+            Metadata { metadata }
+        }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_setCoolDownMillis.
+    pub fn set_cooldown_millis(&mut self, cooldown_millis: i64) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_setCoolDownMillis(self.metadata, cooldown_millis) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_getCoolDownMillis.
+    pub fn get_cooldown_millis(&self) -> i64 {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_getCoolDownMillis(self.metadata) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_setTimeoutMillis.
+    pub fn set_timeout_millis(&mut self, timeout_millis: i64) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_setTimeoutMillis(self.metadata, timeout_millis) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_getTimeoutMillis.
+    pub fn get_timeout_millis(&self) -> i64 {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_getTimeoutMillis(self.metadata) }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_setAdditiveFields.
+    pub fn set_additive_fields(&mut self, additive_fields: &mut Vec<i32>) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe {
+            AStatsManager_PullAtomMetadata_setAdditiveFields(
+                self.metadata,
+                additive_fields.as_mut_ptr(),
+                additive_fields.len().try_into().expect("Cannot convert length to i32"),
+            )
+        }
+    }
+
+    /// Calls AStatsManager_PullAtomMetadata_getAdditiveFields.
+    pub fn get_additive_fields(&self) -> Vec<i32> {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        // We call getNumAdditiveFields to ensure we pass getAdditiveFields a large enough array.
+        unsafe {
+            let num_fields = AStatsManager_PullAtomMetadata_getNumAdditiveFields(self.metadata)
+                .try_into()
+                .expect("Cannot convert num additive fields to usize");
+            let mut fields = vec![0; num_fields];
+            AStatsManager_PullAtomMetadata_getAdditiveFields(self.metadata, fields.as_mut_ptr());
+            fields
+        }
+    }
+}
+
+impl Drop for Metadata {
+    fn drop(&mut self) {
+        // Safety: Metadata::new ensures that self.metadata is a valid object.
+        unsafe { AStatsManager_PullAtomMetadata_release(self.metadata) }
+    }
+}
+
+impl Default for Metadata {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+lazy_static! {
+    static ref COOKIES: Mutex<HashMap<i32, fn() -> StatsPullResult>> = Mutex::new(HashMap::new());
+}
+
+// Safety: We store our callbacks in the global so they are valid.
+unsafe extern "C" fn callback_wrapper(
+    atom_tag: i32,
+    data: *mut AStatsEventList,
+    _cookie: *mut c_void,
+) -> AStatsManager_PullAtomCallbackReturn {
+    if !data.is_null() {
+        let map = COOKIES.lock().unwrap();
+        let cb = map.get(&atom_tag);
+        match cb {
+            None => log::error!("No callback found for {}", atom_tag),
+            Some(cb) => {
+                let stats = cb();
+                let result = stats
+                    .iter()
+                    .map(|stat| stat.add_astats_event(&mut *data))
+                    .collect::<Result<Vec<()>, StatsError>>();
+                match result {
+                    Ok(_) => {
+                        return AStatsManager_PULL_SUCCESS as AStatsManager_PullAtomCallbackReturn
+                    }
+                    _ => log::error!("Error adding astats events: {:?}", result),
+                }
+            }
+        }
+    }
+    AStatsManager_PULL_SKIP as AStatsManager_PullAtomCallbackReturn
+}
+
+/// Rust wrapper for AStatsManager_setPullAtomCallback.
+pub fn set_pull_atom_callback(
+    atom: Atoms,
+    metadata: Option<&Metadata>,
+    callback: fn() -> StatsPullResult,
+) {
+    COOKIES.lock().unwrap().insert(atom as i32, callback);
+    let metadata_raw = match metadata {
+        Some(m) => m.metadata,
+        None => std::ptr::null_mut(),
+    };
+    // Safety: We pass a valid function as the callback.
+    unsafe {
+        AStatsManager_setPullAtomCallback(
+            atom as i32,
+            metadata_raw,
+            Some(callback_wrapper),
+            std::ptr::null_mut(),
+        );
+    }
+}
+
+/// Rust wrapper for AStatsManager_clearPullAtomCallback.
+pub fn clear_pull_atom_callback(atom: Atoms) {
+    COOKIES.lock().unwrap().remove(&(atom as i32));
+    // Safety: No memory allocations.
+    unsafe { AStatsManager_clearPullAtomCallback(atom as i32) }
+}
diff --git a/libstats/pull_rust/statslog.h b/libstats/pull_rust/statslog.h
new file mode 100644
index 0000000..983fb7b
--- /dev/null
+++ b/libstats/pull_rust/statslog.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "stats_pull_atom_callback.h"
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 8e45226..b57e287 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -443,6 +443,20 @@
     refs->mBase->onFirstRef();
 }
 
+void RefBase::incStrongRequireStrong(const void* id) const {
+    weakref_impl* const refs = mRefs;
+    refs->incWeak(id);
+
+    refs->addStrongRef(id);
+    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
+
+    LOG_ALWAYS_FATAL_IF(c <= 0 || c == INITIAL_STRONG_VALUE,
+                        "incStrongRequireStrong() called on %p which isn't already owned", refs);
+#if PRINT_REFS
+    ALOGD("incStrong (requiring strong) of %p from %p: cnt=%d\n", this, id, c);
+#endif
+}
+
 void RefBase::decStrong(const void* id) const
 {
     weakref_impl* const refs = mRefs;
@@ -521,6 +535,14 @@
     ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
 }
 
+void RefBase::weakref_type::incWeakRequireWeak(const void* id)
+{
+    weakref_impl* const impl = static_cast<weakref_impl*>(this);
+    impl->addWeakRef(id);
+    const int32_t c __unused = impl->mWeak.fetch_add(1,
+            std::memory_order_relaxed);
+    LOG_ALWAYS_FATAL_IF(c <= 0, "incWeakRequireWeak called on %p which has no weak refs", this);
+}
 
 void RefBase::weakref_type::decWeak(const void* id)
 {
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index c9b4894..93f9654 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -241,6 +241,30 @@
     ASSERT_FALSE(wp1 != wp2);
 }
 
+TEST(RefBase, AssertWeakRefExistsSuccess) {
+    bool isDeleted;
+    sp<Foo> foo = sp<Foo>::make(&isDeleted);
+    wp<Foo> weakFoo = foo;
+
+    EXPECT_EQ(weakFoo, wp<Foo>::fromExisting(foo.get()));
+    EXPECT_EQ(weakFoo.unsafe_get(), wp<Foo>::fromExisting(foo.get()).unsafe_get());
+
+    EXPECT_FALSE(isDeleted);
+    foo = nullptr;
+    EXPECT_TRUE(isDeleted);
+}
+
+TEST(RefBase, AssertWeakRefExistsDeath) {
+    // uses some other refcounting method, or none at all
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+
+    // can only get a valid wp<> object when you construct it from an sp<>
+    EXPECT_DEATH(wp<Foo>::fromExisting(foo), "");
+
+    delete foo;
+}
+
 // Set up a situation in which we race with visit2AndRremove() to delete
 // 2 strong references.  Bar destructor checks that there are no early
 // deletions and prior updates are visible to destructor.
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 70bf5a0..e3e5f11 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -390,28 +390,6 @@
     return static_cast<size_t>(*(p - 1));
 }
 
-status_t String16::makeLower()
-{
-    const size_t N = size();
-    const char16_t* str = string();
-    char16_t* edited = nullptr;
-    for (size_t i=0; i<N; i++) {
-        const char16_t v = str[i];
-        if (v >= 'A' && v <= 'Z') {
-            if (!edited) {
-                SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
-                if (!buf) {
-                    return NO_MEMORY;
-                }
-                edited = (char16_t*)buf->data();
-                mString = str = edited;
-            }
-            edited[i] = tolower((char)v);
-        }
-    }
-    return OK;
-}
-
 status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
 {
     const size_t N = size();
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
index 63c2800..defa0f5 100644
--- a/libutils/String16_fuzz.cpp
+++ b/libutils/String16_fuzz.cpp
@@ -34,11 +34,6 @@
                     str1.size();
                 }),
 
-                // Casing
-                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
-                    str1.makeLower();
-                }),
-
                 // Comparison
                 ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
                     str1.startsWith(str2);
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index 2505f44..c2e9b02 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -97,13 +97,6 @@
     EXPECT_STR16EQ(u" m", tmp);
 }
 
-TEST(String16Test, MakeLower) {
-    String16 tmp("Verify Me!");
-    tmp.makeLower();
-    EXPECT_EQ(10U, tmp.size());
-    EXPECT_STR16EQ(u"verify me!", tmp);
-}
-
 TEST(String16Test, ReplaceAll) {
     String16 tmp("Verify verify Verify");
     tmp.replaceAll(u'r', u'!');
@@ -176,14 +169,6 @@
     EXPECT_FALSE(tmp.isStaticString());
 }
 
-TEST(String16Test, StaticStringMakeLower) {
-    StaticString16 tmp(u"Verify me!");
-    tmp.makeLower();
-    EXPECT_EQ(10U, tmp.size());
-    EXPECT_STR16EQ(u"verify me!", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
 TEST(String16Test, StaticStringReplaceAll) {
     StaticString16 tmp(u"Verify verify Verify");
     tmp.replaceAll(u'r', u'!');
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 3dc2026..fad130b 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -415,50 +415,28 @@
 
 void String8::toLower()
 {
-    toLower(0, size());
-}
+    const size_t length = size();
+    if (length == 0) return;
 
-void String8::toLower(size_t start, size_t length)
-{
-    const size_t len = size();
-    if (start >= len) {
-        return;
-    }
-    if (start+length > len) {
-        length = len-start;
-    }
-    char* buf = lockBuffer(len);
-    buf += start;
-    while (length > 0) {
+    char* buf = lockBuffer(length);
+    for (size_t i = length; i > 0; --i) {
         *buf = static_cast<char>(tolower(*buf));
         buf++;
-        length--;
     }
-    unlockBuffer(len);
+    unlockBuffer(length);
 }
 
 void String8::toUpper()
 {
-    toUpper(0, size());
-}
+    const size_t length = size();
+    if (length == 0) return;
 
-void String8::toUpper(size_t start, size_t length)
-{
-    const size_t len = size();
-    if (start >= len) {
-        return;
-    }
-    if (start+length > len) {
-        length = len-start;
-    }
-    char* buf = lockBuffer(len);
-    buf += start;
-    while (length > 0) {
+    char* buf = lockBuffer(length);
+    for (size_t i = length; i > 0; --i) {
         *buf = static_cast<char>(toupper(*buf));
         buf++;
-        length--;
     }
-    unlockBuffer(len);
+    unlockBuffer(length);
 }
 
 // ---------------------------------------------------------------------------
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index d37c1de..29f6bd4 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -21,8 +21,8 @@
 
 using namespace android;
 
-class SPFoo : public LightRefBase<SPFoo> {
-public:
+class SPFoo : virtual public RefBase {
+  public:
     explicit SPFoo(bool* deleted_check) : mDeleted(deleted_check) {
         *mDeleted = false;
     }
@@ -69,3 +69,14 @@
     ASSERT_NE(nullptr, foo);
     ASSERT_NE(foo, nullptr);
 }
+
+TEST(StrongPointer, AssertStrongRefExists) {
+    // uses some other refcounting method, or non at all
+    bool isDeleted;
+    SPFoo* foo = new SPFoo(&isDeleted);
+
+    // can only get a valid sp<> object when you construct it as an sp<> object
+    EXPECT_DEATH(sp<SPFoo>::fromExisting(foo), "");
+
+    delete foo;
+}
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e7acd17..7148949 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -140,7 +140,9 @@
 // count, and accidentally passed to f(sp<T>), a strong pointer to the object
 // will be temporarily constructed and destroyed, prematurely deallocating the
 // object, and resulting in heap corruption. None of this would be easily
-// visible in the source.
+// visible in the source. See below on
+// ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION for a compile time
+// option which helps avoid this case.
 
 // Extra Features:
 
@@ -167,6 +169,42 @@
 // to THE SAME sp<> or wp<>.  In effect, their thread-safety properties are
 // exactly like those of T*, NOT atomic<T*>.
 
+// Safety option: ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION
+//
+// This flag makes the semantics for using a RefBase object with wp<> and sp<>
+// much stricter by disabling implicit conversion from raw pointers to these
+// objects. In order to use this, apply this flag in Android.bp like so:
+//
+//    cflags: [
+//        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+//    ],
+//
+// REGARDLESS of whether this flag is on, best usage of sp<> is shown below. If
+// this flag is on, no other usage is possible (directly calling RefBase methods
+// is possible, but seeing code using 'incStrong' instead of 'sp<>', for
+// instance, should already set off big alarm bells. With carefully constructed
+// data structures, it should NEVER be necessary to directly use RefBase
+// methods). Proper RefBase usage:
+//
+//    class Foo : virtual public RefBase { ... };
+//
+//    // always construct an sp object with sp::make
+//    sp<Foo> myFoo = sp<Foo>::make(/*args*/);
+//
+//    // if you need a weak pointer, it must be constructed from a strong
+//    // pointer
+//    wp<Foo> weakFoo = myFoo; // NOT myFoo.get()
+//
+//    // If you are inside of a method of Foo and need access to a strong
+//    // explicitly call this function. This documents your intention to code
+//    // readers, and it will give a runtime error for what otherwise would
+//    // be potential double ownership
+//    .... Foo::someMethod(...) {
+//        // asserts if there is a memory issue
+//        sp<Foo> thiz = sp<Foo>::fromExisting(this);
+//    }
+//
+
 #ifndef ANDROID_REF_BASE_H
 #define ANDROID_REF_BASE_H
 
@@ -244,6 +282,7 @@
 {
 public:
             void            incStrong(const void* id) const;
+            void            incStrongRequireStrong(const void* id) const;
             void            decStrong(const void* id) const;
     
             void            forceIncStrong(const void* id) const;
@@ -257,6 +296,7 @@
         RefBase*            refBase() const;
 
         void                incWeak(const void* id);
+        void                incWeakRequireWeak(const void* id);
         void                decWeak(const void* id);
 
         // acquires a strong reference if there is already one.
@@ -365,10 +405,24 @@
 
     inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
 
+    // if nullptr, returns nullptr
+    //
+    // if a weak pointer is already available, this will retrieve it,
+    // otherwise, this will abort
+    static inline wp<T> fromExisting(T* other);
+
+    // for more information about this flag, see above
+#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
+    wp(std::nullptr_t) : wp() {}
+#else
     wp(T* other);  // NOLINT(implicit)
+#endif
     wp(const wp<T>& other);
     explicit wp(const sp<T>& other);
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> wp(U* other);  // NOLINT(implicit)
+#endif
     template<typename U> wp(const sp<U>& other);  // NOLINT(implicit)
     template<typename U> wp(const wp<U>& other);  // NOLINT(implicit)
 
@@ -376,11 +430,15 @@
 
     // Assignment
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     wp& operator = (T* other);
+#endif
     wp& operator = (const wp<T>& other);
     wp& operator = (const sp<T>& other);
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> wp& operator = (U* other);
+#endif
     template<typename U> wp& operator = (const wp<U>& other);
     template<typename U> wp& operator = (const sp<U>& other);
 
@@ -481,12 +539,27 @@
 // Note that the above comparison operations go out of their way to provide an ordering consistent
 // with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
 
+template <typename T>
+wp<T> wp<T>::fromExisting(T* other) {
+    if (!other) return nullptr;
+
+    auto refs = other->getWeakRefs();
+    refs->incWeakRequireWeak(other);
+
+    wp<T> ret;
+    ret.m_ptr = other;
+    ret.m_refs = refs;
+    return ret;
+}
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 wp<T>::wp(T* other)
     : m_ptr(other)
 {
     m_refs = other ? m_refs = other->createWeak(this) : nullptr;
 }
+#endif
 
 template<typename T>
 wp<T>::wp(const wp<T>& other)
@@ -502,12 +575,14 @@
     m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 wp<T>::wp(U* other)
     : m_ptr(other)
 {
     m_refs = other ? other->createWeak(this) : nullptr;
 }
+#endif
 
 template<typename T> template<typename U>
 wp<T>::wp(const wp<U>& other)
@@ -534,6 +609,7 @@
     if (m_ptr) m_refs->decWeak(this);
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 wp<T>& wp<T>::operator = (T* other)
 {
@@ -544,6 +620,7 @@
     m_refs = newRefs;
     return *this;
 }
+#endif
 
 template<typename T>
 wp<T>& wp<T>::operator = (const wp<T>& other)
@@ -569,6 +646,7 @@
     return *this;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 wp<T>& wp<T>::operator = (U* other)
 {
@@ -579,6 +657,7 @@
     m_refs = newRefs;
     return *this;
 }
+#endif
 
 template<typename T> template<typename U>
 wp<T>& wp<T>::operator = (const wp<U>& other)
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 1a4b47e..5ce48c6 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -85,8 +85,6 @@
 
             bool                contains(const char16_t* chrs) const;
 
-            status_t            makeLower();
-
             status_t            replaceAll(char16_t replaceThis,
                                            char16_t withThis);
 
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 0bcb716..84f14d0 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -130,9 +130,7 @@
             bool                removeAll(const char* other);
 
             void                toLower();
-            void                toLower(size_t start, size_t numChars);
             void                toUpper();
-            void                toUpper(size_t start, size_t numChars);
 
 
     /*
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 11128f2..dd53b9e 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -32,30 +32,67 @@
 public:
     inline sp() : m_ptr(nullptr) { }
 
-    // TODO: switch everyone to using this over new, and make RefBase operator
-    // new private to that class so that we can avoid RefBase being used with
-    // other memory management mechanisms.
+    // The old way of using sp<> was like this. This is bad because it relies
+    // on implicit conversion to sp<>, which we would like to remove (if an
+    // object is being managed some other way, this is double-ownership). We
+    // want to move away from this:
+    //
+    //     sp<Foo> foo = new Foo(...); // DO NOT DO THIS
+    //
+    // Instead, prefer to do this:
+    //
+    //     sp<Foo> foo = sp<Foo>::make(...); // DO THIS
+    //
+    // Sometimes, in order to use this, when a constructor is marked as private,
+    // you may need to add this to your class:
+    //
+    //     friend class sp<Foo>;
     template <typename... Args>
     static inline sp<T> make(Args&&... args);
 
+    // if nullptr, returns nullptr
+    //
+    // if a strong pointer is already available, this will retrieve it,
+    // otherwise, this will abort
+    static inline sp<T> fromExisting(T* other);
+
+    // for more information about this macro and correct RefBase usage, see
+    // the comment at the top of utils/RefBase.h
+#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
+    sp(std::nullptr_t) : sp() {}
+#else
     sp(T* other);  // NOLINT(implicit)
+#endif
     sp(const sp<T>& other);
     sp(sp<T>&& other) noexcept;
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> sp(U* other);  // NOLINT(implicit)
+#endif
     template<typename U> sp(const sp<U>& other);  // NOLINT(implicit)
     template<typename U> sp(sp<U>&& other);  // NOLINT(implicit)
 
+    // Cast a strong pointer directly from one type to another. Constructors
+    // allow changing types, but only if they are pointer-compatible. This does
+    // a static_cast internally.
+    template <typename U>
+    static inline sp<T> cast(const sp<U>& other);
+
     ~sp();
 
     // Assignment
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     sp& operator = (T* other);
+#endif
     sp& operator = (const sp<T>& other);
     sp& operator=(sp<T>&& other) noexcept;
 
     template<typename U> sp& operator = (const sp<U>& other);
     template<typename U> sp& operator = (sp<U>&& other);
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
     template<typename U> sp& operator = (U* other);
+#endif
 
     //! Special optimization for use by ProcessState (and nobody else).
     void force_set(T* other);
@@ -189,6 +226,19 @@
     return result;
 }
 
+template <typename T>
+sp<T> sp<T>::fromExisting(T* other) {
+    if (other) {
+        check_not_on_stack(other);
+        other->incStrongRequireStrong(other);
+        sp<T> result;
+        result.m_ptr = other;
+        return result;
+    }
+    return nullptr;
+}
+
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 sp<T>::sp(T* other)
         : m_ptr(other) {
@@ -197,6 +247,7 @@
         other->incStrong(this);
     }
 }
+#endif
 
 template<typename T>
 sp<T>::sp(const sp<T>& other)
@@ -210,6 +261,7 @@
     other.m_ptr = nullptr;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 sp<T>::sp(U* other)
         : m_ptr(other) {
@@ -218,6 +270,7 @@
         (static_cast<T*>(other))->incStrong(this);
     }
 }
+#endif
 
 template<typename T> template<typename U>
 sp<T>::sp(const sp<U>& other)
@@ -232,6 +285,12 @@
     other.m_ptr = nullptr;
 }
 
+template <typename T>
+template <typename U>
+sp<T> sp<T>::cast(const sp<U>& other) {
+    return sp<T>::fromExisting(static_cast<T*>(other.get()));
+}
+
 template<typename T>
 sp<T>::~sp() {
     if (m_ptr)
@@ -260,6 +319,7 @@
     return *this;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T>
 sp<T>& sp<T>::operator =(T* other) {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
@@ -272,6 +332,7 @@
     m_ptr = other;
     return *this;
 }
+#endif
 
 template<typename T> template<typename U>
 sp<T>& sp<T>::operator =(const sp<U>& other) {
@@ -294,6 +355,7 @@
     return *this;
 }
 
+#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
 template<typename T> template<typename U>
 sp<T>& sp<T>::operator =(U* other) {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
@@ -303,6 +365,7 @@
     m_ptr = other;
     return *this;
 }
+#endif
 
 template<typename T>
 void sp<T>::force_set(T* other) {
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 83cb6ff..d9f5526 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -1,8 +1,7 @@
 {
   "requireLibs": [
-    // Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-    "libdexfile_external.so",
-    "libdexfiled_external.so",
+    "libdexfile.so",
+    "libdexfiled.so",
     "libnativebridge.so",
     "libnativehelper.so",
     "libnativeloader.so",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ce2dd9e..5a9cc99 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -821,6 +821,9 @@
     mkdir /data/ss 0700 system system encryption=Require
 
     mkdir /data/system 0775 system system encryption=Require
+    mkdir /data/system/environ 0700 system system
+    # b/183861600 attempt to fix selinux label before running derive_classpath service
+    restorecon /data/system/environ
     mkdir /data/system/dropbox 0700 system system
     mkdir /data/system/heapdump 0700 system system
     mkdir /data/system/users 0775 system system
@@ -884,10 +887,6 @@
     wait_for_prop apexd.status activated
     perform_apex_config
 
-    # Export *CLASSPATH variables from /etc/classpath
-    # TODO(b/180105615): export from the generated file instead.
-    load_exports /etc/classpath
-
     # Special-case /data/media/obb per b/64566063
     mkdir /data/media 0770 media_rw media_rw encryption=None
     exec - media_rw media_rw -- /system/bin/chattr +F /data/media
@@ -900,8 +899,15 @@
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
+    # Define and export *CLASSPATH variables
+    # Must start before 'odsign', as odsign depends on *CLASSPATH variables
+    exec_start derive_classpath
+    load_exports /data/system/environ/classpath
+    rm /data/system/environ/classpath
+
     # Start the on-device signing daemon, and wait for it to finish, to ensure
     # ART artifacts are generated if needed.
+    # Must start after 'derive_classpath' to have *CLASSPATH variables set.
     start odsign
 
     # Before we can lock keys and proceed to the next boot stage, wait for
@@ -979,7 +985,7 @@
     write /proc/sys/vm/dirty_expire_centisecs 200
     write /proc/sys/vm/dirty_background_ratio  5
 
-on property:sys.boot_completed=1 && property:init.mount_debugfs=1
+on property:sys.boot_completed=1 && property:ro.product.enforce_debugfs_restrictions=true
    umount /sys/kernel/debug
 
 on boot
@@ -1257,7 +1263,7 @@
 on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
   setprop sys.init.userspace_reboot.in_progress ""
 
-on early-init && property:init.mount_debugfs=1
+on early-init && property:ro.product.enforce_debugfs_restrictions=true
     mount debugfs debugfs /sys/kernel/debug
     chmod 0755 /sys/kernel/debug
 
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 27b05ec..0730cce 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -19,6 +19,9 @@
     updatable
     seclabel u:r:adbd:s0
 
+on property:vendor.sys.usb.adb.disabled=*
+    setprop sys.usb.adb.disabled ${vendor.sys.usb.adb.disabled}
+
 # Set default value on sys.usb.configfs early in boot sequence. It will be
 # overridden in `on boot` action of init.hardware.rc.
 on init
diff --git a/toolbox/OWNERS b/toolbox/OWNERS
index 7529cb9..5e2c581 100644
--- a/toolbox/OWNERS
+++ b/toolbox/OWNERS
@@ -1 +1,2 @@
 include platform/system/core:/janitors/OWNERS
+per-file modprobe.c=willmcvicker@google.com,dvander@google.com
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 7df7b71..711586a 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -215,10 +215,7 @@
         return EXIT_FAILURE;
     }
 
-    Modprobe m(mod_dirs);
-    if (blocklist) {
-        m.EnableBlocklist(true);
-    }
+    Modprobe m(mod_dirs, "modules.load", blocklist);
 
     for (const auto& module : modules) {
         switch (mode) {
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 8ab6303..4aca375 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -96,13 +96,13 @@
 
     unique_fd file_fd(TEMP_FAILURE_RETRY(open(file_name, O_RDONLY)));
     if (!file_fd.ok()) {
-        fprintf(stderr, "Error opening file '%s': %s\n", file_name, strerror(errno));
+        PLOG(ERROR) << "Error opening file " << file_name;
         return {};
     }
 
     rc = fstat64(file_fd, &st);
     if (rc < 0) {
-        fprintf(stderr, "Error calling stat on file '%s': %s\n", file_name, strerror(errno));
+        PLOG(ERROR) << "Error calling stat on file '" << file_name << "'";
         return {};
     }
 
@@ -115,14 +115,14 @@
         file_page_offset = page_size - file_page_offset;
     }
     if (__builtin_add_overflow(file_size, file_page_offset, &file_page_size)) {
-        fprintf(stderr, "Failed to page-align file size\n");
+        LOG(ERROR) << "Failed to page-align file size";
         return {};
     }
 
     BufferAllocator alloc;
     unique_fd dmabuf_fd(alloc.Alloc(kDmabufSystemHeapName, file_page_size));
     if (!dmabuf_fd.ok()) {
-        fprintf(stderr, "Error creating dmabuf: %d\n", dmabuf_fd.get());
+        LOG(ERROR) << "Error creating dmabuf: " << dmabuf_fd.get();
         return dmabuf_fd;
     }
 
@@ -137,12 +137,12 @@
                 pread(file_fd, (char*)shm + file_offset, file_size - file_offset, file_offset));
 
         if (num_read < 0) {
-            fprintf(stderr, "Error reading package file '%s': %s\n", file_name, strerror(errno));
+            PLOG(ERROR) << "Error reading package file '" << file_name << "'";
             break;
         }
 
         if (num_read == 0) {
-            fprintf(stderr, "Unexpected end of file '%s'\n", file_name);
+            LOG(ERROR) << "Unexpected end of file '" << file_name << "'";
             break;
         }
 
@@ -182,17 +182,17 @@
     struct apploader_resp resp;
     ssize_t rc = read(tipc_fd, &resp, sizeof(resp));
     if (rc < 0) {
-        fprintf(stderr, "Failed to read response: %zd\n", rc);
+        PLOG(ERROR) << "Failed to read response";
         return rc;
     }
 
     if (rc < sizeof(resp)) {
-        fprintf(stderr, "Not enough data in response: %zd\n", rc);
+        LOG(ERROR) << "Not enough data in response: " << rc;
         return -EIO;
     }
 
     if (resp.hdr.cmd != (APPLOADER_CMD_LOAD_APPLICATION | APPLOADER_RESP_BIT)) {
-        fprintf(stderr, "Invalid command in response: %u\n", resp.hdr.cmd);
+        LOG(ERROR) << "Invalid command in response: " << resp.hdr.cmd;
         return -EINVAL;
     }
 
@@ -200,28 +200,28 @@
         case APPLOADER_NO_ERROR:
             break;
         case APPLOADER_ERR_UNKNOWN_CMD:
-            fprintf(stderr, "Error: unknown command\n");
+            LOG(ERROR) << "Error: unknown command";
             break;
         case APPLOADER_ERR_INVALID_CMD:
-            fprintf(stderr, "Error: invalid command arguments\n");
+            LOG(ERROR) << "Error: invalid command arguments";
             break;
         case APPLOADER_ERR_NO_MEMORY:
-            fprintf(stderr, "Error: out of Trusty memory\n");
+            LOG(ERROR) << "Error: out of Trusty memory";
             break;
         case APPLOADER_ERR_VERIFICATION_FAILED:
-            fprintf(stderr, "Error: failed to verify the package\n");
+            LOG(ERROR) << "Error: failed to verify the package";
             break;
         case APPLOADER_ERR_LOADING_FAILED:
-            fprintf(stderr, "Error: failed to load the package\n");
+            LOG(ERROR) << "Error: failed to load the package";
             break;
         case APPLOADER_ERR_ALREADY_EXISTS:
-            fprintf(stderr, "Error: application already exists\n");
+            LOG(ERROR) << "Error: application already exists";
             break;
         case APPLOADER_ERR_INTERNAL:
-            fprintf(stderr, "Error: internal apploader error\n");
+            LOG(ERROR) << "Error: internal apploader error";
             break;
         default:
-            fprintf(stderr, "Unrecognized error: %u\n", resp.error);
+            LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
     }
 
@@ -241,14 +241,14 @@
 
     tipc_fd = tipc_connect(dev_name, APPLOADER_PORT);
     if (tipc_fd < 0) {
-        fprintf(stderr, "Failed to connect to Trusty app loader: %s\n", strerror(-tipc_fd));
+        LOG(ERROR) << "Failed to connect to Trusty app loader: " << strerror(-tipc_fd);
         rc = tipc_fd;
         goto err_tipc_connect;
     }
 
     rc = send_load_message(tipc_fd, package_fd, package_size);
     if (rc < 0) {
-        fprintf(stderr, "Failed to send package: %zd\n", rc);
+        LOG(ERROR) << "Failed to send package: " << rc;
         goto err_send;
     }
 
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 94aedd7..29c6f93 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -914,7 +914,7 @@
     }
 
     size_t buf_size = PAGE_SIZE * num_pages;
-    dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0);
+    dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
     if (dma_buf < 0) {
         ret = dma_buf;
         fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);