Merge changes I262391dd,I18aad4a5,I0786e475

* changes:
  snapuserd: Move socket and io_uring status out of core and into the server.
  snapuserd: Rename UserSnapshotDmUserHandler to HandlerThread.
  snapuserd: Split out the implementation into a libsnapuserd component.
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index cbb1181..442392d 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -665,7 +665,7 @@
         "* Process %d has been suspended while crashing.\n"
         "* To attach the debugger, run this on the host:\n"
         "*\n"
-        "*     gdbclient.py -p %d\n"
+        "*     lldbclient.py -p %d\n"
         "*\n"
         "***********************************************************",
         target_process, target_process);
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 4eb7382..4043a6e 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -159,7 +159,8 @@
 }
 
 noinline void fprintf_null() {
-    fprintf(nullptr, "oops");
+    FILE* sneaky_null = nullptr;
+    fprintf(sneaky_null, "oops");
 }
 
 noinline void readdir_null() {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 895c111..517f2df 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -642,7 +642,7 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
 
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 8 \(SEGV_MTEAERR\), fault addr --------)");
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code [89] \(SEGV_MTE[AS]ERR\), fault addr)");
 #else
   GTEST_SKIP() << "Requires aarch64";
 #endif
@@ -1680,6 +1680,24 @@
   if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
   p[params.access_offset] = 42;
   if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
+
+  bool recoverable = std::get<1>(GetParam());
+  ASSERT_TRUE(recoverable);  // Non-recoverable should have crashed.
+
+  // As we're in recoverable mode, trigger another 2x use-after-frees (ensuring
+  // we end with at least one in a different slot), make sure the process still
+  // doesn't crash.
+  p = reinterpret_cast<char* volatile>(malloc(params.alloc_size));
+  char* volatile p2 = reinterpret_cast<char* volatile>(malloc(params.alloc_size));
+  free(static_cast<void*>(const_cast<char*>(p)));
+  free(static_cast<void*>(const_cast<char*>(p2)));
+  *p = 42;
+  *p2 = 42;
+
+  // Under clang coverage (which is a default TEST_MAPPING presubmit target), the
+  // recoverable+seccomp tests fail because the minijail prevents some atexit syscalls that clang
+  // coverage does. Thus, skip the atexit handlers.
+  _exit(0);
 }
 
 TEST_F(CrasherTest, fdsan_warning_abort_message) {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 799c9f9..699d406 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -128,25 +128,6 @@
     int64_t image_size;
 };
 
-enum class ImageType {
-    // Must be flashed for device to boot into the kernel.
-    BootCritical,
-    // Normal partition to be flashed during "flashall".
-    Normal,
-    // Partition that is never flashed during "flashall".
-    Extra
-};
-
-struct Image {
-    std::string nickname;
-    std::string img_name;
-    std::string sig_name;
-    std::string part_name;
-    bool optional_if_no_image;
-    ImageType type;
-    bool IsSecondary() const { return nickname.empty(); }
-};
-
 static std::vector<Image> images = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, ImageType::BootCritical },
@@ -336,36 +317,34 @@
     return -1;
 }
 
-struct NetworkSerial {
+Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial) {
     Socket::Protocol protocol;
-    std::string address;
-    int port;
-};
+    const char* net_address = nullptr;
+    int port = 0;
 
-static Result<NetworkSerial> ParseNetworkSerial(const std::string& serial) {
-    const auto serial_parsed = android::base::Tokenize(serial, ":");
-    const auto parsed_segments_count = serial_parsed.size();
-    if (parsed_segments_count != 2 && parsed_segments_count != 3) {
-        return Error() << "invalid network address: " << serial << ". Expected format:\n"
-                       << "<protocol>:<address>:<port> (tcp:localhost:5554)";
-    }
-
-    Socket::Protocol protocol;
-    if (serial_parsed[0] == "tcp") {
+    if (android::base::StartsWith(serial, "tcp:")) {
         protocol = Socket::Protocol::kTcp;
-    } else if (serial_parsed[0] == "udp") {
+        net_address = serial.c_str() + strlen("tcp:");
+        port = tcp::kDefaultPort;
+    } else if (android::base::StartsWith(serial, "udp:")) {
         protocol = Socket::Protocol::kUdp;
+        net_address = serial.c_str() + strlen("udp:");
+        port = udp::kDefaultPort;
     } else {
-        return Error() << "invalid network address: " << serial << ". Expected format:\n"
-                       << "<protocol>:<address>:<port> (tcp:localhost:5554)";
+        return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX)
+               << "protocol prefix ('tcp:' or 'udp:') is missed: " << serial << ". "
+               << "Expected address format:\n"
+               << "<protocol>:<address>:<port> (tcp:localhost:5554)";
     }
 
-    int port = 5554;
-    if (parsed_segments_count == 3) {
-        android::base::ParseInt(serial_parsed[2], &port, 5554);
+    std::string error;
+    std::string host;
+    if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
+        return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS)
+               << "invalid network address '" << net_address << "': " << error;
     }
 
-    return NetworkSerial{protocol, serial_parsed[1], port};
+    return NetworkSerial{protocol, host, port};
 }
 
 // Opens a new Transport connected to the particular device.
@@ -380,7 +359,7 @@
 // object, and the caller should not attempt to delete the returned Transport.
 static Transport* open_device(const char* local_serial, bool wait_for_device = true,
                               bool announce = true) {
-    const Result<NetworkSerial> network_serial = ParseNetworkSerial(local_serial);
+    const Result<NetworkSerial, FastbootError> network_serial = ParseNetworkSerial(local_serial);
 
     Transport* transport = nullptr;
     while (true) {
@@ -397,8 +376,13 @@
             if (transport == nullptr && announce) {
                 LOG(ERROR) << "error: " << error;
             }
-        } else {
+        } else if (network_serial.error().code() ==
+                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX) {
+            // WRONG_PREFIX is special because it happens when user wants to communicate with USB
+            // device
             transport = usb_open(match_fastboot(local_serial));
+        } else {
+            Expect(network_serial);
         }
 
         if (transport != nullptr) {
@@ -413,7 +397,7 @@
             announce = false;
             LOG(ERROR) << "< waiting for " << local_serial << ">";
         }
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+        std::this_thread::sleep_for(std::chrono::seconds(1));
     }
 }
 
@@ -476,7 +460,7 @@
             announce = false;
             LOG(ERROR) << "< waiting for any device >";
         }
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+        std::this_thread::sleep_for(std::chrono::seconds(1));
     }
 }
 
@@ -487,7 +471,7 @@
     }
 
     const char* local_serial = *argv;
-    EXPECT(ParseNetworkSerial(local_serial));
+    Expect(ParseNetworkSerial(local_serial));
 
     const Transport* transport = open_device(local_serial, false);
     if (transport == nullptr) {
@@ -506,7 +490,7 @@
 }
 
 static int Disconnect(const char* local_serial) {
-    EXPECT(ParseNetworkSerial(local_serial));
+    Expect(ParseNetworkSerial(local_serial));
 
     ConnectedDevicesStorage storage;
     {
@@ -998,7 +982,7 @@
     fprintf(stderr, "--------------------------------------------\n");
 }
 
-static std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size) {
+std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size) {
     if (max_size <= 0 || max_size > std::numeric_limits<uint32_t>::max()) {
         die("invalid max size %" PRId64, max_size);
     }
@@ -1043,7 +1027,7 @@
     return value;
 }
 
-static int64_t get_sparse_limit(int64_t size) {
+int64_t get_sparse_limit(int64_t size) {
     int64_t limit = sparse_limit;
     if (limit == 0) {
         // Unlimited, so see what the target device's limit is.
@@ -1099,7 +1083,9 @@
     unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
 
     if (fd == -1) {
-        return false;
+        auto path = find_item_given_name(fname);
+        fd = unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
+        if (fd == -1) return false;
     }
 
     struct stat s;
@@ -1175,7 +1161,7 @@
 
 // Note: this only works in userspace fastboot. In the bootloader, use
 // should_flash_in_userspace().
-static bool is_logical(const std::string& partition) {
+bool is_logical(const std::string& partition) {
     std::string value;
     return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
 }
@@ -1257,8 +1243,7 @@
     lseek(buf->fd.get(), 0, SEEK_SET);
 }
 
-static void flash_partition_files(const std::string& partition,
-                                  const std::vector<SparsePtr>& files) {
+void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files) {
     for (size_t i = 0; i < files.size(); i++) {
         sparse_file* s = files[i].get();
         int64_t sz = sparse_file_len(s, true, false);
@@ -1317,7 +1302,7 @@
     return count;
 }
 
-static bool supports_AB() {
+bool supports_AB() {
     return get_slot_count() >= 2;
 }
 
@@ -1440,7 +1425,7 @@
     }
 }
 
-static bool is_retrofit_device() {
+bool is_retrofit_device() {
     std::string value;
     if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
         return false;
@@ -1555,7 +1540,7 @@
     delete old_transport;
 
     // Give the current connection time to close.
-    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+    std::this_thread::sleep_for(std::chrono::seconds(1));
 
     fb->set_transport(open_device());
 
@@ -1576,10 +1561,23 @@
     }
 }
 
+std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot) {
+    auto slot = entry.second;
+    if (slot.empty()) {
+        slot = current_slot;
+    }
+    if (slot.empty()) {
+        return entry.first->part_name;
+    }
+    if (slot == "all") {
+        LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
+    }
+    return entry.first->part_name + "_" + slot;
+}
+
 class FlashAllTool {
   public:
-    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
-                 bool wipe, bool force_flash);
+    FlashAllTool(FlashingPlan* fp);
 
     void Flash();
 
@@ -1589,35 +1587,18 @@
     void CollectImages();
     void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
     void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
-    void UpdateSuperPartition();
-    bool OptimizedFlashSuper();
 
     // If the image uses the default slot, or the user specified "all", then
     // the paired string will be empty. If the image requests a specific slot
     // (for example, system_other) it is specified instead.
     using ImageEntry = std::pair<const Image*, std::string>;
 
-    std::string GetPartitionName(const ImageEntry& entry);
-
-    const ImageSource& source_;
-    std::string slot_override_;
-    bool skip_secondary_;
-    bool wipe_;
-    bool force_flash_;
-    std::string current_slot_;
-    std::string secondary_slot_;
-
     std::vector<ImageEntry> boot_images_;
     std::vector<ImageEntry> os_images_;
+    FlashingPlan* fp_;
 };
 
-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),
-      force_flash_(force_flash) {}
+FlashAllTool::FlashAllTool(FlashingPlan* fp) : fp_(fp) {}
 
 void FlashAllTool::Flash() {
     DumpInfo();
@@ -1625,10 +1606,10 @@
 
     // Change the slot first, so we boot into the correct recovery image when
     // using fastbootd.
-    if (slot_override_ == "all") {
+    if (fp_->slot == "all") {
         set_active("a");
     } else {
-        set_active(slot_override_);
+        set_active(fp_->slot);
     }
 
     DetermineSlot();
@@ -1640,131 +1621,80 @@
     // or in bootloader fastboot.
     FlashImages(boot_images_);
 
-    if (!OptimizedFlashSuper()) {
-        // Sync the super partition. This will reboot to userspace fastboot if needed.
-        UpdateSuperPartition();
+    auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_);
 
+    if (flash_super_task) {
+        flash_super_task->Run();
+    } else {
+        // Sync the super partition. This will reboot to userspace fastboot if needed.
+        std::unique_ptr<UpdateSuperTask> update_super_task = std::make_unique<UpdateSuperTask>(fp_);
+        update_super_task->Run();
         // Resize any logical partition to 0, so each partition is reset to 0
         // extents, and will achieve more optimal allocation.
+        std::vector<std::unique_ptr<ResizeTask>> resize_tasks;
         for (const auto& [image, slot] : os_images_) {
-            auto resize_partition = [](const std::string& partition) -> void {
-                if (is_logical(partition)) {
-                    fb->ResizePartition(partition, "0");
+            // Retrofit devices have two super partitions, named super_a and super_b.
+            // On these devices, secondary slots must be flashed as physical
+            // partitions (otherwise they would not mount on first boot). To enforce
+            // this, we delete any logical partitions for the "other" slot.
+            if (is_retrofit_device()) {
+                std::string partition_name = image->part_name + "_"s + slot;
+                if (image->IsSecondary() && is_logical(partition_name)) {
+                    fp_->fb->DeletePartition(partition_name);
+                    std::unique_ptr<DeleteTask> delete_task =
+                            std::make_unique<DeleteTask>(fp_, partition_name);
+                    delete_task->Run();
                 }
-            };
-            do_for_partitions(image->part_name, slot, resize_partition, false);
+            }
+            resize_tasks.emplace_back(
+                    std::make_unique<ResizeTask>(fp_, image->part_name, "0", slot));
+        }
+        for (auto& i : resize_tasks) {
+            i->Run();
         }
     }
-
-    // Flash OS images, resizing logical partitions as needed.
     FlashImages(os_images_);
 }
 
-bool FlashAllTool::OptimizedFlashSuper() {
-    if (!supports_AB()) {
-        LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
-        return false;
-    }
-    if (slot_override_ == "all") {
-        LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
-        return false;
-    }
-
-    // Does this device use dynamic partitions at all?
-    unique_fd fd = source_.OpenFile("super_empty.img");
-    if (fd < 0) {
-        LOG(VERBOSE) << "could not open super_empty.img";
-        return false;
-    }
-
-    // Try to find whether there is a super partition.
-    std::string super_name;
-    if (fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
-        super_name = "super";
-    }
-    std::string partition_size_str;
-    if (fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
-        LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
-        return false;
-    }
-
-    SuperFlashHelper helper(source_);
-    if (!helper.Open(fd)) {
-        return false;
-    }
-
-    for (const auto& entry : os_images_) {
-        auto partition = GetPartitionName(entry);
-        auto image = entry.first;
-
-        if (!helper.AddPartition(partition, image->img_name, image->optional_if_no_image)) {
-            return false;
-        }
-    }
-
-    auto s = helper.GetSparseLayout();
-    if (!s) {
-        return false;
-    }
-
-    std::vector<SparsePtr> files;
-    if (int limit = get_sparse_limit(sparse_file_len(s.get(), false, false))) {
-        files = resparse_file(s.get(), limit);
-    } else {
-        files.emplace_back(std::move(s));
-    }
-
-    // Send the data to the device.
-    flash_partition_files(super_name, files);
-
-    // Remove images that we already flashed, just in case we have non-dynamic OS images.
-    auto remove_if_callback = [&, this](const ImageEntry& entry) -> bool {
-        return helper.WillFlash(GetPartitionName(entry));
-    };
-    os_images_.erase(std::remove_if(os_images_.begin(), os_images_.end(), remove_if_callback),
-                     os_images_.end());
-    return true;
-}
-
 void FlashAllTool::CheckRequirements() {
     std::vector<char> contents;
-    if (!source_.ReadFile("android-info.txt", &contents)) {
+    if (!fp_->source->ReadFile("android-info.txt", &contents)) {
         die("could not read android-info.txt");
     }
-    ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
+    ::CheckRequirements({contents.data(), contents.size()}, fp_->force_flash);
 }
 
 void FlashAllTool::DetermineSlot() {
-    if (slot_override_.empty()) {
-        current_slot_ = get_current_slot();
+    if (fp_->slot.empty()) {
+        fp_->current_slot = get_current_slot();
     } else {
-        current_slot_ = slot_override_;
+        fp_->current_slot = fp_->slot;
     }
 
-    if (skip_secondary_) {
+    if (fp_->skip_secondary) {
         return;
     }
-    if (slot_override_ != "" && slot_override_ != "all") {
-        secondary_slot_ = get_other_slot(slot_override_);
+    if (fp_->slot != "" && fp_->slot != "all") {
+        fp_->secondary_slot = get_other_slot(fp_->slot);
     } else {
-        secondary_slot_ = get_other_slot();
+        fp_->secondary_slot = get_other_slot();
     }
-    if (secondary_slot_ == "") {
+    if (fp_->secondary_slot == "") {
         if (supports_AB()) {
             fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
         }
-        skip_secondary_ = true;
+        fp_->skip_secondary = true;
     }
 }
 
 void FlashAllTool::CollectImages() {
     for (size_t i = 0; i < images.size(); ++i) {
-        std::string slot = slot_override_;
+        std::string slot = fp_->slot;
         if (images[i].IsSecondary()) {
-            if (skip_secondary_) {
+            if (fp_->skip_secondary) {
                 continue;
             }
-            slot = secondary_slot_;
+            slot = fp_->secondary_slot;
         }
         if (images[i].type == ImageType::BootCritical) {
             boot_images_.emplace_back(&images[i], slot);
@@ -1777,7 +1707,7 @@
 void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
     for (const auto& [image, slot] : images) {
         fastboot_buffer buf;
-        unique_fd fd = source_.OpenFile(image->img_name);
+        unique_fd fd = fp_->source->OpenFile(image->img_name);
         if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
             if (image->optional_if_no_image) {
                 continue;
@@ -1791,7 +1721,7 @@
 void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
     auto flash = [&, this](const std::string& partition_name) {
         std::vector<char> signature_data;
-        if (source_.ReadFile(image.sig_name, &signature_data)) {
+        if (fp_->source->ReadFile(image.sig_name, &signature_data)) {
             fb->Download("signature", signature_data);
             fb->RawCommand("signature", "installing signature");
         }
@@ -1804,55 +1734,6 @@
     do_for_partitions(image.part_name, slot, flash, false);
 }
 
-void FlashAllTool::UpdateSuperPartition() {
-    unique_fd fd = source_.OpenFile("super_empty.img");
-    if (fd < 0) {
-        return;
-    }
-    if (!is_userspace_fastboot()) {
-        reboot_to_userspace_fastboot();
-    }
-
-    std::string super_name;
-    if (fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
-        super_name = "super";
-    }
-    fb->Download(super_name, fd, get_file_size(fd));
-
-    std::string command = "update-super:" + super_name;
-    if (wipe_) {
-        command += ":wipe";
-    }
-    fb->RawCommand(command, "Updating super partition");
-
-    // Retrofit devices have two super partitions, named super_a and super_b.
-    // On these devices, secondary slots must be flashed as physical
-    // partitions (otherwise they would not mount on first boot). To enforce
-    // this, we delete any logical partitions for the "other" slot.
-    if (is_retrofit_device()) {
-        for (const auto& [image, slot] : os_images_) {
-            std::string partition_name = image->part_name + "_"s + slot;
-            if (image->IsSecondary() && is_logical(partition_name)) {
-                fb->DeletePartition(partition_name);
-            }
-        }
-    }
-}
-
-std::string FlashAllTool::GetPartitionName(const ImageEntry& entry) {
-    auto slot = entry.second;
-    if (slot.empty()) {
-        slot = current_slot_;
-    }
-    if (slot.empty()) {
-        return entry.first->part_name;
-    }
-    if (slot == "all") {
-        LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
-    }
-    return entry.first->part_name + "_" + slot;
-}
-
 class ZipImageSource final : public ImageSource {
   public:
     explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
@@ -1871,15 +1752,16 @@
     return unzip_to_file(zip_, name.c_str());
 }
 
-static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary,
-                      bool force_flash) {
+static void do_update(const char* filename, FlashingPlan* fp) {
     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, force_flash);
+    ZipImageSource zp = ZipImageSource(zip);
+    fp->source = &zp;
+    fp->wants_wipe = false;
+    FlashAllTool tool(fp);
     tool.Flash();
 
     CloseArchive(zip);
@@ -1904,9 +1786,10 @@
     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,
-                        bool force_flash) {
-    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe, force_flash);
+static void do_flashall(FlashingPlan* fp) {
+    LocalImageSource s = LocalImageSource();
+    fp->source = &s;
+    FlashAllTool tool(fp);
     tool.Flash();
 }
 
@@ -2141,16 +2024,8 @@
 
 int FastBootTool::Main(int argc, char* argv[]) {
     android::base::InitLogging(argv, FastbootLogger, FastbootAborter);
+    std::unique_ptr<FlashingPlan> fp = std::make_unique<FlashingPlan>();
 
-    bool wants_wipe = false;
-    bool wants_reboot = false;
-    bool wants_reboot_bootloader = false;
-    bool wants_reboot_recovery = false;
-    bool wants_reboot_fastboot = false;
-    bool skip_reboot = false;
-    bool wants_set_active = false;
-    bool skip_secondary = false;
-    bool force_flash = false;
     unsigned fs_options = 0;
     int longindex;
     std::string slot_override;
@@ -2203,7 +2078,7 @@
             } else if (name == "disable-verity") {
                 g_disable_verity = true;
             } else if (name == "force") {
-                force_flash = true;
+                fp->force_flash = true;
             } else if (name == "fs-options") {
                 fs_options = ParseFsOption(optarg);
             } else if (name == "header-version") {
@@ -2222,9 +2097,9 @@
             } else if (name == "ramdisk-offset") {
                 g_boot_img_hdr.ramdisk_addr = strtoul(optarg, 0, 16);
             } else if (name == "skip-reboot") {
-                skip_reboot = true;
+                fp->skip_reboot = true;
             } else if (name == "skip-secondary") {
-                skip_secondary = true;
+                fp->skip_secondary = true;
             } else if (name == "slot") {
                 slot_override = optarg;
             } else if (name == "dtb-offset") {
@@ -2245,7 +2120,7 @@
         } else {
             switch (c) {
                 case 'a':
-                    wants_set_active = true;
+                    fp->wants_set_active = true;
                     if (optarg) next_active = optarg;
                     break;
                 case 'h':
@@ -2265,7 +2140,7 @@
                     set_verbose();
                     break;
                 case 'w':
-                    wants_wipe = true;
+                    fp->wants_wipe = true;
                     break;
                 case '?':
                     return 1;
@@ -2278,7 +2153,7 @@
     argc -= optind;
     argv += optind;
 
-    if (argc == 0 && !wants_wipe && !wants_set_active) syntax_error("no command");
+    if (argc == 0 && !fp->wants_wipe && !fp->wants_set_active) syntax_error("no command");
 
     if (argc > 0 && !strcmp(*argv, "devices")) {
         list_devices();
@@ -2314,13 +2189,14 @@
 
     fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
     fb = &fastboot_driver;
+    fp->fb = &fastboot_driver;
 
     const double start = now();
 
     if (slot_override != "") slot_override = verify_slot(slot_override);
     if (next_active != "") next_active = verify_slot(next_active, false);
 
-    if (wants_set_active) {
+    if (fp->wants_set_active) {
         if (next_active == "") {
             if (slot_override == "") {
                 std::string current_slot;
@@ -2328,14 +2204,14 @@
                     if (current_slot[0] == '_') current_slot.erase(0, 1);
                     next_active = verify_slot(current_slot, false);
                 } else {
-                    wants_set_active = false;
+                    fp->wants_set_active = false;
                 }
             } else {
                 next_active = verify_slot(slot_override, false);
             }
         }
     }
-
+    std::unique_ptr<Task> reboot_task = nullptr;
     std::vector<std::string> args(argv, argv + argc);
     while (!args.empty()) {
         std::string command = next_arg(&args);
@@ -2387,30 +2263,19 @@
             fb->Download("signature", data);
             fb->RawCommand("signature", "installing signature");
         } else if (command == FB_CMD_REBOOT) {
-            wants_reboot = true;
-
             if (args.size() == 1) {
-                std::string what = next_arg(&args);
-                if (what == "bootloader") {
-                    wants_reboot = false;
-                    wants_reboot_bootloader = true;
-                } else if (what == "recovery") {
-                    wants_reboot = false;
-                    wants_reboot_recovery = true;
-                } else if (what == "fastboot") {
-                    wants_reboot = false;
-                    wants_reboot_fastboot = true;
-                } else {
-                    syntax_error("unknown reboot target %s", what.c_str());
-                }
+                std::string reboot_target = next_arg(&args);
+                reboot_task = std::make_unique<RebootTask>(fp.get(), reboot_target);
+            } else {
+                reboot_task = std::make_unique<RebootTask>(fp.get());
             }
             if (!args.empty()) syntax_error("junk after reboot command");
         } else if (command == FB_CMD_REBOOT_BOOTLOADER) {
-            wants_reboot_bootloader = true;
+            reboot_task = std::make_unique<RebootTask>(fp.get(), "bootloader");
         } else if (command == FB_CMD_REBOOT_RECOVERY) {
-            wants_reboot_recovery = true;
+            reboot_task = std::make_unique<RebootTask>(fp.get(), "recovery");
         } else if (command == FB_CMD_REBOOT_FASTBOOT) {
-            wants_reboot_fastboot = true;
+            reboot_task = std::make_unique<RebootTask>(fp.get(), "fastboot");
         } else if (command == FB_CMD_CONTINUE) {
             fb->Continue();
         } else if (command == FB_CMD_BOOT) {
@@ -2431,7 +2296,7 @@
                 fname = find_item(pname);
             }
             if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
-            FlashTask task(slot_override, force_flash, pname, fname);
+            FlashTask task(slot_override, pname, fname);
             task.Run();
         } else if (command == "flash:raw") {
             std::string partition = next_arg(&args);
@@ -2450,11 +2315,12 @@
             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, force_flash);
+                fp->skip_secondary = true;
+                do_flashall(fp.get());
             } else {
-                do_flashall(slot_override, skip_secondary, wants_wipe, force_flash);
+                do_flashall(fp.get());
             }
-            wants_reboot = true;
+            reboot_task = std::make_unique<RebootTask>(fp.get());
         } else if (command == "update") {
             bool slot_all = (slot_override == "all");
             if (slot_all) {
@@ -2465,8 +2331,8 @@
             if (!args.empty()) {
                 filename = next_arg(&args);
             }
-            do_update(filename.c_str(), slot_override, skip_secondary || slot_all, force_flash);
-            wants_reboot = true;
+            do_update(filename.c_str(), fp.get());
+            reboot_task = std::make_unique<RebootTask>(fp.get());
         } else if (command == FB_CMD_SET_ACTIVE) {
             std::string slot = verify_slot(next_arg(&args), false);
             fb->SetActive(slot);
@@ -2499,11 +2365,14 @@
             fb->CreatePartition(partition, size);
         } else if (command == FB_CMD_DELETE_PARTITION) {
             std::string partition = next_arg(&args);
+            auto delete_task = std::make_unique<DeleteTask>(fp.get(), partition);
             fb->DeletePartition(partition);
         } else if (command == FB_CMD_RESIZE_PARTITION) {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb->ResizePartition(partition, size);
+            std::unique_ptr<ResizeTask> resize_task =
+                    std::make_unique<ResizeTask>(fp.get(), partition, size, slot_override);
+            resize_task->Run();
         } else if (command == "gsi") {
             std::string arg = next_arg(&args);
             if (arg == "wipe") {
@@ -2538,9 +2407,8 @@
             syntax_error("unknown command %s", command.c_str());
         }
     }
-
-    if (wants_wipe) {
-        if (force_flash) {
+    if (fp->wants_wipe) {
+        if (fp->force_flash) {
             CancelSnapshotIfNeeded();
         }
         std::vector<std::string> partitions = {"userdata", "cache", "metadata"};
@@ -2554,22 +2422,12 @@
             fb_perform_format(partition, 1, partition_type, "", fs_options);
         }
     }
-    if (wants_set_active) {
+    if (fp->wants_set_active) {
         fb->SetActive(next_active);
     }
-    if (wants_reboot && !skip_reboot) {
-        fb->Reboot();
-        fb->WaitForDisconnect();
-    } else if (wants_reboot_bootloader) {
-        fb->RebootTo("bootloader");
-        fb->WaitForDisconnect();
-    } else if (wants_reboot_recovery) {
-        fb->RebootTo("recovery");
-        fb->WaitForDisconnect();
-    } else if (wants_reboot_fastboot) {
-        reboot_to_userspace_fastboot();
+    if (reboot_task && !fp->skip_reboot) {
+        reboot_task->Run();
     }
-
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
 
     auto* old_transport = fb->set_transport(nullptr);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index b5fb8c0..2264ca3 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -28,9 +28,16 @@
 #pragma once
 
 #include <string>
+#include "fastboot_driver.h"
+#include "super_flash_helper.h"
+#include "util.h"
 
 #include <bootimg.h>
 
+#include "result.h"
+#include "socket.h"
+#include "util.h"
+
 class FastBootTool {
   public:
     int Main(int argc, char* argv[]);
@@ -40,6 +47,44 @@
     unsigned ParseFsOption(const char*);
 };
 
+enum class ImageType {
+    // Must be flashed for device to boot into the kernel.
+    BootCritical,
+    // Normal partition to be flashed during "flashall".
+    Normal,
+    // Partition that is never flashed during "flashall".
+    Extra
+};
+
+struct Image {
+    std::string nickname;
+    std::string img_name;
+    std::string sig_name;
+    std::string part_name;
+    bool optional_if_no_image;
+    ImageType type;
+    bool IsSecondary() const { return nickname.empty(); }
+};
+
+using ImageEntry = std::pair<const Image*, std::string>;
+
+struct FlashingPlan {
+    // If the image uses the default slot, or the user specified "all", then
+    // the paired string will be empty. If the image requests a specific slot
+    // (for example, system_other) it is specified instead.
+    ImageSource* source;
+    bool wants_wipe = false;
+    bool skip_reboot = false;
+    bool wants_set_active = false;
+    bool skip_secondary = false;
+    bool force_flash = false;
+
+    std::string slot;
+    std::string current_slot;
+    std::string secondary_slot;
+    fastboot::FastBootDriver* fb;
+};
+
 bool should_flash_in_userspace(const std::string& partition_name);
 bool is_userspace_fastboot();
 void do_flash(const char* pname, const char* fname);
@@ -48,3 +93,19 @@
 std::string find_item(const std::string& item);
 void reboot_to_userspace_fastboot();
 void syntax_error(const char* fmt, ...);
+
+struct NetworkSerial {
+    Socket::Protocol protocol;
+    std::string address;
+    int port;
+};
+
+Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
+bool supports_AB();
+std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot_);
+void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
+int64_t get_sparse_limit(int64_t size);
+std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
+
+bool is_retrofit_device();
+bool is_logical(const std::string& partition);
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 79f37fd..1863e95 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -203,6 +203,54 @@
     ParseRequirementLineTestMalformed("require-for-product :");
 }
 
+static void ParseNetworkSerialTest(const std::string& description, const std::string& serial,
+                                   const std::string& expected_address,
+                                   const Socket::Protocol expected_protocol,
+                                   const int expected_port) {
+    const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);
+
+    ASSERT_RESULT_OK(parsed) << description;
+
+    const NetworkSerial network_serial = parsed.value();
+    EXPECT_EQ(network_serial.address, expected_address) << description;
+    EXPECT_EQ(network_serial.protocol, expected_protocol) << description;
+    EXPECT_EQ(network_serial.port, expected_port) << description;
+}
+
+static void ParseNetworkSerialNegativeTest(const std::string& description,
+                                           const std::string& serial,
+                                           const FastbootError::Type expected_error) {
+    const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);
+
+    EXPECT_FALSE(parsed.ok()) << description;
+    EXPECT_EQ(parsed.error().code(), expected_error) << description;
+}
+
+TEST(FastBoot, ParseNetworkSerial) {
+    ParseNetworkSerialTest("tcp IPv4 parsed", "tcp:192.168.1.0", "192.168.1.0",
+                           Socket::Protocol::kTcp, 5554);
+
+    ParseNetworkSerialTest("udp IPv4 parsed", "udp:192.168.1.0", "192.168.1.0",
+                           Socket::Protocol::kUdp, 5554);
+
+    ParseNetworkSerialTest("port parsed", "udp:192.168.1.0:9999", "192.168.1.0",
+                           Socket::Protocol::kUdp, 9999);
+
+    ParseNetworkSerialTest("IPv6 parsed", "tcp:2001:db8:3333:4444:5555:6666:7777:8888",
+                           "2001:db8:3333:4444:5555:6666:7777:8888", Socket::Protocol::kTcp, 5554);
+
+    ParseNetworkSerialTest("empty IPv6 parsed", "tcp:::", "::", Socket::Protocol::kTcp, 5554);
+
+    ParseNetworkSerialNegativeTest("wrong prefix", "tcpa:192.168.1.0",
+                                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);
+
+    ParseNetworkSerialNegativeTest("no prefix", "192.168.1.0",
+                                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);
+
+    ParseNetworkSerialNegativeTest("wrong port", "tcp:192.168.1.0:-1",
+                                   FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS);
+}
+
 int main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     android::base::InitLogging(argv);
diff --git a/fastboot/result.h b/fastboot/result.h
new file mode 100644
index 0000000..dfa5e10
--- /dev/null
+++ b/fastboot/result.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <android-base/result.h>
+
+#include "util.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::ResultError;
+
+class FastbootError {
+  public:
+    enum Type { NETWORK_SERIAL_WRONG_PREFIX = 1, NETWORK_SERIAL_WRONG_ADDRESS = 2 };
+
+    FastbootError(Type&& type) : type_(std::forward<Type>(type)) {}
+
+    Type value() const { return type_; }
+    operator Type() const { return value(); }
+    std::string print() const { return ""; }
+
+  private:
+    Type type_;
+};
+
+template <typename T, typename U>
+inline T Expect(Result<T, U> r) {
+    if (r.ok()) {
+        return r.value();
+    }
+
+    die(r.error().message());
+
+    return r.value();
+}
\ No newline at end of file
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 3f33c76..6233c90 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -14,24 +14,23 @@
 // limitations under the License.
 //
 #include "task.h"
-
+#include <iostream>
 #include "fastboot.h"
-#include "util.h"
+#include "filesystem.h"
+#include "super_flash_helper.h"
 
-FlashTask::FlashTask(const std::string& _slot) : slot_(_slot){};
-FlashTask::FlashTask(const std::string& _slot, bool _force_flash)
-    : slot_(_slot), force_flash_(_force_flash) {}
-FlashTask::FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname)
-    : pname_(_pname), fname_(find_item(_pname)), slot_(_slot), force_flash_(_force_flash) {
+using namespace std::string_literals;
+
+FlashTask::FlashTask(const std::string& slot, const std::string& pname)
+    : pname_(pname), fname_(find_item(pname)), slot_(slot) {
     if (fname_.empty()) die("cannot determine image filename for '%s'", pname_.c_str());
 }
-FlashTask::FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname,
-                     const std::string& _fname)
-    : pname_(_pname), fname_(_fname), slot_(_slot), force_flash_(_force_flash) {}
+FlashTask::FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname)
+    : pname_(_pname), fname_(_fname), slot_(_slot) {}
 
 void FlashTask::Run() {
     auto flash = [&](const std::string& partition) {
-        if (should_flash_in_userspace(partition) && !is_userspace_fastboot() && !force_flash_) {
+        if (should_flash_in_userspace(partition) && !is_userspace_fastboot()) {
             die("The partition you are trying to flash is dynamic, and "
                 "should be flashed via fastbootd. Please run:\n"
                 "\n"
@@ -44,3 +43,146 @@
     };
     do_for_partitions(pname_, slot_, flash, true);
 }
+
+RebootTask::RebootTask(FlashingPlan* fp) : fp_(fp){};
+RebootTask::RebootTask(FlashingPlan* fp, const std::string& reboot_target)
+    : reboot_target_(reboot_target), fp_(fp){};
+
+void RebootTask::Run() {
+    if ((reboot_target_ == "userspace" || reboot_target_ == "fastboot")) {
+        if (!is_userspace_fastboot()) {
+            reboot_to_userspace_fastboot();
+            fp_->fb->WaitForDisconnect();
+        }
+    } else if (reboot_target_ == "recovery") {
+        fp_->fb->RebootTo("recovery");
+        fp_->fb->WaitForDisconnect();
+    } else if (reboot_target_ == "bootloader") {
+        fp_->fb->RebootTo("bootloader");
+        fp_->fb->WaitForDisconnect();
+    } else if (reboot_target_ == "") {
+        fp_->fb->Reboot();
+        fp_->fb->WaitForDisconnect();
+    } else {
+        syntax_error("unknown reboot target %s", reboot_target_.c_str());
+    }
+}
+
+FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
+                                           std::unique_ptr<SuperFlashHelper> helper,
+                                           SparsePtr sparse_layout)
+    : super_name_(super_name),
+      helper_(std::move(helper)),
+      sparse_layout_(std::move(sparse_layout)) {}
+
+void FlashSuperLayoutTask::Run() {
+    std::vector<SparsePtr> files;
+    if (int limit = get_sparse_limit(sparse_file_len(sparse_layout_.get(), false, false))) {
+        files = resparse_file(sparse_layout_.get(), limit);
+    } else {
+        files.emplace_back(std::move(sparse_layout_));
+    }
+
+    // Send the data to the device.
+    flash_partition_files(super_name_, files);
+}
+
+std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::Initialize(
+        FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
+    if (!supports_AB()) {
+        LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
+        return nullptr;
+    }
+    if (fp->slot == "all") {
+        LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
+        return nullptr;
+    }
+
+    // Does this device use dynamic partitions at all?
+    unique_fd fd = fp->source->OpenFile("super_empty.img");
+
+    if (fd < 0) {
+        LOG(VERBOSE) << "could not open super_empty.img";
+        return nullptr;
+    }
+
+    std::string super_name;
+    // Try to find whether there is a super partition.
+    if (fp->fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
+        super_name = "super";
+    }
+    std::string partition_size_str;
+
+    if (fp->fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
+        LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
+        return nullptr;
+    }
+    std::unique_ptr<SuperFlashHelper> helper = std::make_unique<SuperFlashHelper>(*fp->source);
+    if (!helper->Open(fd)) {
+        return nullptr;
+    }
+
+    for (const auto& entry : os_images) {
+        auto partition = GetPartitionName(entry, fp->current_slot);
+        auto image = entry.first;
+
+        if (!helper->AddPartition(partition, image->img_name, image->optional_if_no_image)) {
+            return nullptr;
+        }
+    }
+
+    auto s = helper->GetSparseLayout();
+    if (!s) return nullptr;
+
+    // Remove images that we already flashed, just in case we have non-dynamic OS images.
+    auto remove_if_callback = [&](const ImageEntry& entry) -> bool {
+        return helper->WillFlash(GetPartitionName(entry, fp->current_slot));
+    };
+    os_images.erase(std::remove_if(os_images.begin(), os_images.end(), remove_if_callback),
+                    os_images.end());
+    return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s));
+}
+
+UpdateSuperTask::UpdateSuperTask(FlashingPlan* fp)
+    : fp_(fp) {}
+
+void UpdateSuperTask::Run() {
+    unique_fd fd = fp_->source->OpenFile("super_empty.img");
+    if (fd < 0) {
+        return;
+    }
+    if (!is_userspace_fastboot()) {
+        reboot_to_userspace_fastboot();
+    }
+
+    std::string super_name;
+    if (fp_->fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
+        super_name = "super";
+    }
+    fp_->fb->Download(super_name, fd, get_file_size(fd));
+
+    std::string command = "update-super:" + super_name;
+    if (fp_->wants_wipe) {
+        command += ":wipe";
+    }
+    fp_->fb->RawCommand(command, "Updating super partition");
+}
+
+ResizeTask::ResizeTask(FlashingPlan* fp, const std::string& pname, const std::string& size,
+                       const std::string& slot)
+    : fp_(fp), pname_(pname), size_(size), slot_(slot) {}
+
+void ResizeTask::Run() {
+    auto resize_partition = [this](const std::string& partition) -> void {
+        if (is_logical(partition)) {
+            fp_->fb->ResizePartition(partition, size_);
+        }
+    };
+    do_for_partitions(pname_, slot_, resize_partition, false);
+}
+
+DeleteTask::DeleteTask(FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
+
+void DeleteTask::Run() {
+    fp_->fb->DeletePartition(pname_);
+}
\ No newline at end of file
diff --git a/fastboot/task.h b/fastboot/task.h
index 216e658..0af771e 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -20,6 +20,7 @@
 
 #include "fastboot.h"
 #include "fastboot_driver.h"
+#include "super_flash_helper.h"
 #include "util.h"
 
 class Task {
@@ -31,18 +32,71 @@
 
 class FlashTask : public Task {
   public:
-    FlashTask(const std::string& _slot);
-    FlashTask(const std::string& _slot, bool _force_flash);
-    FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname);
-    FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname,
-              const std::string& _fname);
+    FlashTask(const std::string& slot, const std::string& pname);
+    FlashTask(const std::string& slot, const std::string& pname, const std::string& fname);
 
     void Run() override;
-    ~FlashTask() {}
 
   private:
     const std::string pname_;
     const std::string fname_;
     const std::string slot_;
-    bool force_flash_ = false;
+};
+
+class RebootTask : public Task {
+  public:
+    RebootTask(FlashingPlan* fp);
+    RebootTask(FlashingPlan* fp, const std::string& reboot_target);
+    void Run() override;
+
+  private:
+    const std::string reboot_target_ = "";
+    const FlashingPlan* fp_;
+};
+
+class FlashSuperLayoutTask : public Task {
+  public:
+    FlashSuperLayoutTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
+                         SparsePtr sparse_layout);
+    static std::unique_ptr<FlashSuperLayoutTask> Initialize(FlashingPlan* fp,
+                                                            std::vector<ImageEntry>& os_images);
+    using ImageEntry = std::pair<const Image*, std::string>;
+    void Run() override;
+
+  private:
+    const std::string super_name_;
+    std::unique_ptr<SuperFlashHelper> helper_;
+    SparsePtr sparse_layout_;
+};
+
+class UpdateSuperTask : public Task {
+  public:
+    UpdateSuperTask(FlashingPlan* fp);
+    void Run() override;
+
+  private:
+    const FlashingPlan* fp_;
+};
+
+class ResizeTask : public Task {
+  public:
+    ResizeTask(FlashingPlan* fp, const std::string& pname, const std::string& size,
+               const std::string& slot);
+    void Run() override;
+
+  private:
+    const FlashingPlan* fp_;
+    const std::string pname_;
+    const std::string size_;
+    const std::string slot_;
+};
+
+class DeleteTask : public Task {
+  public:
+    DeleteTask(FlashingPlan* _fp, const std::string& _pname);
+    void Run() override;
+
+  private:
+    const FlashingPlan* fp_;
+    const std::string pname_;
 };
diff --git a/fastboot/util.h b/fastboot/util.h
index bc01473..290d0d5 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -6,21 +6,11 @@
 #include <string>
 #include <vector>
 
-#include <android-base/logging.h>
-#include <android-base/result.h>
 #include <android-base/unique_fd.h>
 #include <bootimg.h>
 #include <liblp/liblp.h>
 #include <sparse/sparse.h>
 
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::base::ResultError;
-
-#define EXPECT(result) \
-    (result.ok() ? result.value() : (LOG(FATAL) << result.error().message(), result.value()))
-
 using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
 
 /* util stuff */
diff --git a/fastboot/vendor_boot_img_utils_test.cpp b/fastboot/vendor_boot_img_utils_test.cpp
index 1563b89..467c6e9 100644
--- a/fastboot/vendor_boot_img_utils_test.cpp
+++ b/fastboot/vendor_boot_img_utils_test.cpp
@@ -73,8 +73,8 @@
 
 // Seek to beginning then read the whole file.
 Result<std::string> ReadStartOfFdToString(borrowed_fd fd, std::filesystem::path path) {
-    if (lseek64(fd.get(), 0, SEEK_SET) != 0)
-        return ErrnoError() << "lseek64(" << path << ", 0, SEEK_SET)";
+    if (lseek(fd.get(), 0, SEEK_SET) != 0)
+        return ErrnoError() << "lseek(" << path << ", 0, SEEK_SET)";
     std::string content;
     if (!android::base::ReadFdToString(fd, &content)) return ErrnoError() << "read(" << path << ")";
     return content;
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index b3763ae..fa04c43 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -108,6 +108,12 @@
 
     // Estimated COW size from OTA manifest.
     uint64 estimated_cow_size = 12;
+
+    // Enable multi-threaded compression
+    bool enable_threading = 13;
+
+    // Enable batching for COW writes
+    bool batched_writes = 14;
 }
 
 // Next: 8
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 949e6c5..bd5c8cb 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -60,6 +60,12 @@
     bool using_snapuserd = false;
     std::string compression_algorithm;
 
+    // True if multi-threaded compression should be enabled
+    bool enable_threading;
+
+    // True if COW writes should be batched in memory
+    bool batched_writes;
+
     struct Return {
         SnapshotStatus snapshot_status;
         std::vector<Interval> cow_partition_usable_regions;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 961db02..f655522 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -400,6 +400,12 @@
     status->set_metadata_sectors(0);
     status->set_using_snapuserd(cow_creator->using_snapuserd);
     status->set_compression_algorithm(cow_creator->compression_algorithm);
+    if (cow_creator->enable_threading) {
+        status->set_enable_threading(cow_creator->enable_threading);
+    }
+    if (cow_creator->batched_writes) {
+        status->set_batched_writes(cow_creator->batched_writes);
+    }
 
     if (!WriteSnapshotStatus(lock, *status)) {
         PLOG(ERROR) << "Could not write snapshot status: " << status->name();
@@ -3210,6 +3216,8 @@
         vabc_disable_reason = "recovery";
     } else if (!cow_format_support) {
         vabc_disable_reason = "cow format not supported";
+    } else if (!KernelSupportsCompressedSnapshots()) {
+        vabc_disable_reason = "kernel missing userspace block device support";
     }
 
     if (!vabc_disable_reason.empty()) {
@@ -3248,6 +3256,12 @@
             .using_snapuserd = using_snapuserd,
             .compression_algorithm = compression_algorithm,
     };
+    if (dap_metadata.vabc_feature_set().has_threaded()) {
+        cow_creator.enable_threading = dap_metadata.vabc_feature_set().threaded();
+    }
+    if (dap_metadata.vabc_feature_set().has_batch_writes()) {
+        cow_creator.batched_writes = dap_metadata.vabc_feature_set().batch_writes();
+    }
 
     auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                              &all_snapshot_status);
@@ -3635,6 +3649,8 @@
     CowOptions cow_options;
     cow_options.compression = status.compression_algorithm();
     cow_options.max_blocks = {status.device_size() / cow_options.block_size};
+    cow_options.batch_write = status.batched_writes();
+    cow_options.num_compress_threads = status.enable_threading() ? 2 : 0;
     // Disable scratch space for vts tests
     if (device()->IsTestDevice()) {
         cow_options.scratch_space = false;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 13314da..460d49d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -124,6 +124,10 @@
         SKIP_IF_NON_VIRTUAL_AB();
 
         SetupProperties();
+        if (!DeviceSupportsMode()) {
+            GTEST_SKIP() << "Mode not supported on this device";
+        }
+
         InitializeState();
         CleanupTestArtifacts();
         FormatFakeSuper();
@@ -159,7 +163,13 @@
         IPropertyFetcher::OverrideForTesting(std::move(fetcher));
 
         if (GetLegacyCompressionEnabledProperty() || CanUseUserspaceSnapshots()) {
-            snapuserd_required_ = true;
+            // If we're asked to test the device's actual configuration, then it
+            // may be misconfigured, so check for kernel support as libsnapshot does.
+            if (FLAGS_force_mode.empty()) {
+                snapuserd_required_ = KernelSupportsCompressedSnapshots();
+            } else {
+                snapuserd_required_ = true;
+            }
         }
     }
 
@@ -176,6 +186,16 @@
         LOG(INFO) << "Teardown complete for test: " << test_name_;
     }
 
+    bool DeviceSupportsMode() {
+        if (FLAGS_force_mode.empty()) {
+            return true;
+        }
+        if (snapuserd_required_ && !KernelSupportsCompressedSnapshots()) {
+            return false;
+        }
+        return true;
+    }
+
     void InitializeState() {
         ASSERT_TRUE(sm->EnsureImageManager());
         image_manager_ = sm->image_manager();
@@ -193,6 +213,11 @@
         // get an accurate list to remove.
         lock_ = nullptr;
 
+        // If there is no image manager, the test was skipped.
+        if (!image_manager_) {
+            return;
+        }
+
         std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
                                               "test_partition_b"};
         for (const auto& snapshot : snapshots) {
@@ -946,6 +971,11 @@
         SKIP_IF_NON_VIRTUAL_AB();
 
         SnapshotTest::SetUp();
+        if (!image_manager_) {
+            // Test was skipped.
+            return;
+        }
+
         Cleanup();
 
         // Cleanup() changes slot suffix, so initialize it again.
@@ -2680,6 +2710,9 @@
         CleanUp();
     }
     void CleanUp() {
+        if (!image_manager_) {
+            return;
+        }
         EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
                     image_manager_->DeleteBackingImage(kImageName));
     }
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
index 69d72e1..cc12d1d 100644
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -71,11 +71,18 @@
     repeated string partition_names = 3;
 }
 
+message VABCFeatureSet {
+  optional bool threaded = 1;
+  optional bool batch_writes = 2;
+}
+
 message DynamicPartitionMetadata {
     repeated DynamicPartitionGroup groups = 1;
     optional bool vabc_enabled = 3;
     optional string vabc_compression_param = 4;
     optional uint32 cow_version = 5;
+    // A collection of knobs to tune Virtual AB Compression
+    optional VABCFeatureSet vabc_feature_set = 6;
 }
 
 message DeltaArchiveManifest {
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index a98bf0e..1ffa89c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -29,6 +29,7 @@
 #include <fs_mgr/roots.h>
 #include <liblp/property_fetcher.h>
 
+using android::dm::DeviceMapper;
 using android::dm::kSectorSize;
 using android::fiemap::FiemapStatus;
 using android::fs_mgr::EnsurePathMounted;
@@ -251,7 +252,10 @@
         LOG(INFO) << "Userspace snapshots disabled for testing";
         return false;
     }
-
+    if (!KernelSupportsCompressedSnapshots()) {
+        LOG(ERROR) << "Userspace snapshots requested, but no kernel support is available.";
+        return false;
+    }
     return true;
 }
 
@@ -278,5 +282,10 @@
     return fetcher->GetBoolProperty("snapuserd.test.dm.snapshots", false);
 }
 
+bool KernelSupportsCompressedSnapshots() {
+    auto& dm = DeviceMapper::Instance();
+    return dm.GetTargetByName("user", nullptr);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 8c4c7c6..370f3c4 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -127,6 +127,8 @@
 void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
                   uint64_t start_block, uint64_t num_blocks);
 
+bool KernelSupportsCompressedSnapshots();
+
 bool GetLegacyCompressionEnabledProperty();
 bool GetUserspaceSnapshotsEnabledProperty();
 bool GetIouringEnabledProperty();
diff --git a/gatekeeperd/OWNERS b/gatekeeperd/OWNERS
index 9c99c6e..04cd19e 100644
--- a/gatekeeperd/OWNERS
+++ b/gatekeeperd/OWNERS
@@ -1,2 +1,5 @@
+# Bug component: 1124862
+drysdale@google.com
+oarbildo@google.com
+subrahmanyaman@google.com
 swillden@google.com
-jdanis@google.com
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 76fcd55..eb43a33 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -151,7 +151,7 @@
     void clear_sid(uint32_t userId) {
         char filename[21];
         snprintf(filename, sizeof(filename), "%u", userId);
-        if (remove(filename) < 0) {
+        if (remove(filename) < 0 && errno != ENOENT) {
             ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
             store_sid(userId, 0);
         }
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index b180a58..66e1e63 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -392,12 +392,13 @@
         mHealthInfo->batteryFullChargeDesignCapacityUah =
                 getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
 
-    if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
-        mHealthInfo->batteryStateOfHealth = getIntField(mHealthdConfig->batteryStateOfHealthPath);
-
     if (!mHealthdConfig->batteryHealthStatusPath.isEmpty())
         mBatteryHealthStatus = getIntField(mHealthdConfig->batteryHealthStatusPath);
 
+    if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+        mHealthInfo->batteryHealthData->batteryStateOfHealth =
+                getIntField(mHealthdConfig->batteryStateOfHealthPath);
+
     if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
         mHealthInfo->batteryHealthData->batteryManufacturingDateSeconds =
                 getIntField(mHealthdConfig->batteryManufacturingDatePath);
@@ -591,6 +592,10 @@
         if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
             return getIntField(mHealthdConfig->batteryFirstUsageDatePath);
     }
+    if (id == BATTERY_PROP_STATE_OF_HEALTH) {
+        if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+            return getIntField(mHealthdConfig->batteryStateOfHealthPath);
+    }
     return 0;
 }
 
@@ -669,6 +674,11 @@
         ret = OK;
         break;
 
+    case BATTERY_PROP_STATE_OF_HEALTH:
+        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_STATE_OF_HEALTH);
+        ret = OK;
+        break;
+
     default:
         break;
     }
diff --git a/init/action.cpp b/init/action.cpp
index 1e998ae..18f6360 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -30,7 +30,7 @@
 
 Result<void> RunBuiltinFunction(const BuiltinFunction& function,
                                 const std::vector<std::string>& args, const std::string& context) {
-    auto builtin_arguments = BuiltinArguments(context);
+    BuiltinArguments builtin_arguments{.context = context};
 
     builtin_arguments.args.resize(args.size());
     builtin_arguments.args[0] = args[0];
@@ -69,7 +69,7 @@
 }
 
 Result<void> Command::CheckCommand() const {
-    auto builtin_arguments = BuiltinArguments("host_init_verifier");
+    BuiltinArguments builtin_arguments{.context = "host_init_verifier"};
 
     builtin_arguments.args.resize(args_.size());
     builtin_arguments.args[0] = args_[0];
diff --git a/init/builtin_arguments.h b/init/builtin_arguments.h
index 1742b78..890a216 100644
--- a/init/builtin_arguments.h
+++ b/init/builtin_arguments.h
@@ -24,10 +24,6 @@
 namespace init {
 
 struct BuiltinArguments {
-    BuiltinArguments(const std::string& context) : context(context) {}
-    BuiltinArguments(std::vector<std::string> args, const std::string& context)
-        : args(std::move(args)), context(context) {}
-
     const std::string& operator[](std::size_t i) const { return args[i]; }
     auto begin() const { return args.begin(); }
     auto end() const { return args.end(); }
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a89813e..bc23972 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1074,7 +1074,7 @@
 static Result<void> do_restorecon_recursive(const BuiltinArguments& args) {
     std::vector<std::string> non_const_args(args.args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
-    return do_restorecon({std::move(non_const_args), args.context});
+    return do_restorecon({.args = std::move(non_const_args), .context = args.context});
 }
 
 static Result<void> do_loglevel(const BuiltinArguments& args) {
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index 481fa31..461ed22 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -85,7 +85,7 @@
 }
 
 Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args) {
-    BuiltinArguments remaining_args(args.context);
+    BuiltinArguments remaining_args{.context = args.context};
 
     remaining_args.args = std::vector<std::string>(args.begin() + 1, args.end());
     remaining_args.args[0] = args[0];
diff --git a/init/init.cpp b/init/init.cpp
index f964c60..be1ebee 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -51,6 +51,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr_vendor_overlay.h>
 #include <keyutils.h>
@@ -211,16 +212,16 @@
     }
 
   private:
-    void ResetWaitForPropLocked() {
+    void ResetWaitForPropLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_) {
         wait_prop_name_.clear();
         wait_prop_value_.clear();
         waiting_for_prop_.reset();
     }
 
     std::mutex lock_;
-    std::unique_ptr<Timer> waiting_for_prop_{nullptr};
-    std::string wait_prop_name_;
-    std::string wait_prop_value_;
+    GUARDED_BY(lock_) std::unique_ptr<Timer> waiting_for_prop_{nullptr};
+    GUARDED_BY(lock_) std::string wait_prop_name_;
+    GUARDED_BY(lock_) std::string wait_prop_value_;
 
 } prop_waiter_state;
 
@@ -246,48 +247,21 @@
         WakeMainInitThread();
     }
 
-    std::optional<std::string> CheckShutdown() {
+    std::optional<std::string> CheckShutdown() __attribute__((warn_unused_result)) {
         auto lock = std::lock_guard{shutdown_command_lock_};
         if (do_shutdown_ && !IsShuttingDown()) {
+            do_shutdown_ = false;
             return shutdown_command_;
         }
         return {};
     }
 
-    bool do_shutdown() const { return do_shutdown_; }
-    void set_do_shutdown(bool value) { do_shutdown_ = value; }
-
   private:
     std::mutex shutdown_command_lock_;
-    std::string shutdown_command_;
+    std::string shutdown_command_ GUARDED_BY(shutdown_command_lock_);
     bool do_shutdown_ = false;
 } shutdown_state;
 
-static void UnwindMainThreadStack() {
-    unwindstack::AndroidLocalUnwinder unwinder;
-    unwindstack::AndroidUnwinderData data;
-    if (!unwinder.Unwind(data)) {
-        LOG(ERROR) << __FUNCTION__
-                   << "sys.powerctl: Failed to unwind callstack: " << data.GetErrorString();
-    }
-    for (const auto& frame : data.frames) {
-        LOG(ERROR) << "sys.powerctl: " << unwinder.FormatFrame(frame);
-    }
-}
-
-void DebugRebootLogging() {
-    LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
-              << " IsShuttingDown: " << IsShuttingDown();
-    if (shutdown_state.do_shutdown()) {
-        LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
-        UnwindMainThreadStack();
-    }
-    if (IsShuttingDown()) {
-        LOG(ERROR) << "sys.powerctl set while init is already shutting down";
-        UnwindMainThreadStack();
-    }
-}
-
 void DumpState() {
     ServiceList::GetInstance().DumpState();
     ActionManager::GetInstance().DumpState();
@@ -1108,36 +1082,43 @@
     // Restore prio before main loop
     setpriority(PRIO_PROCESS, 0, 0);
     while (true) {
-        // By default, sleep until something happens.
-        std::optional<std::chrono::milliseconds> epoll_timeout;
+        // By default, sleep until something happens. Do not convert far_future into
+        // std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock
+        // is 1ns.
+        const boot_clock::time_point far_future = boot_clock::time_point::max();
+        boot_clock::time_point next_action_time = far_future;
 
         auto shutdown_command = shutdown_state.CheckShutdown();
         if (shutdown_command) {
             LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                       << "' Calling HandlePowerctlMessage()";
             HandlePowerctlMessage(*shutdown_command);
-            shutdown_state.set_do_shutdown(false);
         }
 
         if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
+            // If there's more work to do, wake up again immediately.
+            if (am.HasMoreCommands()) {
+                next_action_time = boot_clock::now();
+            }
         }
+        // Since the above code examined pending actions, no new actions must be
+        // queued by the code between this line and the Epoll::Wait() call below
+        // without calling WakeMainInitThread().
         if (!IsShuttingDown()) {
             auto next_process_action_time = HandleProcessActions();
 
             // If there's a process that needs restarting, wake up in time for that.
             if (next_process_action_time) {
-                epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
-                        *next_process_action_time - boot_clock::now());
-                if (epoll_timeout < 0ms) epoll_timeout = 0ms;
+                next_action_time = std::min(next_action_time, *next_process_action_time);
             }
         }
 
-        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
-            // If there's more work to do, wake up again immediately.
-            if (am.HasMoreCommands()) epoll_timeout = 0ms;
+        std::optional<std::chrono::milliseconds> epoll_timeout;
+        if (next_action_time != far_future) {
+            epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+                    std::max(next_action_time - boot_clock::now(), 0ns));
         }
-
         auto epoll_result = epoll.Wait(epoll_timeout);
         if (!epoll_result.ok()) {
             LOG(ERROR) << epoll_result.error();
diff --git a/init/init.h b/init/init.h
index 063632a..9c7e918 100644
--- a/init/init.h
+++ b/init/init.h
@@ -42,8 +42,6 @@
 void PropertyChanged(const std::string& name, const std::string& value);
 bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);
 
-void DebugRebootLogging();
-
 int SecondStageMain(int argc, char** argv);
 
 int StopServicesFromApex(const std::string& apex_name);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 87ffdb9..8da6982 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -550,9 +550,6 @@
         }
         LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                   << process_log_string;
-        if (!value.empty()) {
-            DebugRebootLogging();
-        }
         if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
             *error = "Userspace reboot is not supported by this device";
             return {PROP_ERROR_INVALID_VALUE};
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 4cc00fe..062ed39 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -897,29 +897,31 @@
             continue;
         }
 
-        auto system_entry = GetEntryForMountPoint(&fstab, "/system");
-        if (!system_entry) {
-            LOG(ERROR) << "Could not find mount entry for /system";
-            break;
-        }
-        if (!system_entry->fs_mgr_flags.logical) {
-            LOG(INFO) << "Skipping mount of " << name << ", system is not dynamic.";
-            break;
-        }
+        auto system_entries = GetEntriesForMountPoint(&fstab, "/system");
+        for (auto& system_entry : system_entries) {
+            if (!system_entry) {
+                LOG(ERROR) << "Could not find mount entry for /system";
+                break;
+            }
+            if (!system_entry->fs_mgr_flags.logical) {
+                LOG(INFO) << "Skipping mount of " << name << ", system is not dynamic.";
+                break;
+            }
 
-        auto entry = *system_entry;
-        auto partition_name = name + fs_mgr_get_slot_suffix();
-        auto replace_name = "system"s + fs_mgr_get_slot_suffix();
+            auto entry = *system_entry;
+            auto partition_name = name + fs_mgr_get_slot_suffix();
+            auto replace_name = "system"s + fs_mgr_get_slot_suffix();
 
-        entry.mount_point = "/"s + name;
-        entry.blk_device =
+            entry.mount_point = "/"s + name;
+            entry.blk_device =
                 android::base::StringReplace(entry.blk_device, replace_name, partition_name, false);
-        if (!fs_mgr_update_logical_partition(&entry)) {
-            LOG(ERROR) << "Could not update logical partition";
-            continue;
-        }
+            if (!fs_mgr_update_logical_partition(&entry)) {
+                LOG(ERROR) << "Could not update logical partition";
+                continue;
+            }
 
-        extra_fstab.emplace_back(std::move(entry));
+            extra_fstab.emplace_back(std::move(entry));
+        }
     }
 
     SkipMountingPartitions(&extra_fstab, true /* verbose */);
diff --git a/init/service.cpp b/init/service.cpp
index 87d9c3a..8456d1e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -447,6 +447,15 @@
         return {};
     }
 
+    // On newer kernels, /dev/console will always exist because
+    // "console=ttynull" is hard-coded in CONFIG_CMDLINE. This new boot
+    // property should be set via "androidboot.serialconsole=0" to explicitly
+    // disable services requiring the console. For older kernels and boot
+    // images, not setting this at all will fall back to the old behavior
+    if (GetProperty("ro.boot.serialconsole", "") == "0") {
+        return {};
+    }
+
     if (proc_attr_.console.empty()) {
         proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
     }
@@ -755,6 +764,9 @@
 
     NotifyStateChange("running");
     reboot_on_failure.Disable();
+
+    LOG(INFO) << "... started service '" << name_ << "' has pid " << pid_;
+
     return {};
 }
 
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1da69ba..38f19ff 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -372,6 +372,7 @@
     std::set<pid_t> pgids;
     pgids.emplace(initialPid);
     std::set<pid_t> pids;
+    int processes = 0;
 
     std::unique_ptr<FILE, decltype(&fclose)> fd(nullptr, fclose);
 
@@ -390,6 +391,7 @@
         pid_t pid;
         bool file_is_empty = true;
         while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
+            processes++;
             file_is_empty = false;
             if (pid == 0) {
                 // Should never happen...  but if it does, trying to kill this
@@ -406,31 +408,25 @@
                 pids.emplace(pid);
             }
         }
-        if (file_is_empty) {
-            // This happens when process is already dead
-            return 0;
-        }
-
-        // Erase all pids that will be killed when we kill the process groups.
-        for (auto it = pids.begin(); it != pids.end();) {
-            pid_t pgid = getpgid(*it);
-            if (pgids.count(pgid) == 1) {
-                it = pids.erase(it);
-            } else {
-                ++it;
+        if (!file_is_empty) {
+            // Erase all pids that will be killed when we kill the process groups.
+            for (auto it = pids.begin(); it != pids.end();) {
+                pid_t pgid = getpgid(*it);
+                if (pgids.count(pgid) == 1) {
+                    it = pids.erase(it);
+                } else {
+                    ++it;
+                }
             }
         }
     }
 
-    int processes = 0;
     // Kill all process groups.
     for (const auto pgid : pgids) {
         LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
                      << " as part of process cgroup " << initialPid;
 
-        if (kill(-pgid, signal) == 0) {
-            processes++;
-        } else if (errno != ESRCH) {
+        if (kill(-pgid, signal) == -1 && errno != ESRCH) {
             PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
         }
     }
@@ -440,9 +436,7 @@
         LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
                      << initialPid;
 
-        if (kill(pid, signal) == 0) {
-            processes++;
-        } else if (errno != ESRCH) {
+        if (kill(pid, signal) == -1 && errno != ESRCH) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
     }
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 35adf36..4db7372 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -815,11 +815,11 @@
                 profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
             }
             if (!profile->ExecuteForProcess(uid, pid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " process profile";
+                LOG(WARNING) << "Failed to apply " << name << " process profile";
                 success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << " process profile";
+            LOG(WARNING) << "Failed to find " << name << " process profile";
             success = false;
         }
     }
@@ -836,11 +836,11 @@
                 profile->EnableResourceCaching(ProfileAction::RCT_TASK);
             }
             if (!profile->ExecuteForTask(tid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " task profile";
+                LOG(WARNING) << "Failed to apply " << name << " task profile";
                 success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << " task profile";
+            LOG(WARNING) << "Failed to find " << name << " task profile";
             success = false;
         }
     }
diff --git a/libutils/ProcessCallStack_fuzz.cpp b/libutils/ProcessCallStack_fuzz.cpp
index 30136cd..552a11e 100644
--- a/libutils/ProcessCallStack_fuzz.cpp
+++ b/libutils/ProcessCallStack_fuzz.cpp
@@ -44,7 +44,7 @@
                 dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));
         std::thread th = std::thread(loop);
         pthread_setname_np(th.native_handle(), threadName.c_str());
-        threads.push_back(move(th));
+        threads.push_back(std::move(th));
     }
 
     // Collect thread information
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
index 69288b3..8291be9 100644
--- a/libutils/RefBase_fuzz.cpp
+++ b/libutils/RefBase_fuzz.cpp
@@ -177,7 +177,7 @@
         uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxOperations);
         std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
         std::thread tmpThread = std::thread(loop, threadOperations);
-        threads.push_back(move(tmpThread));
+        threads.push_back(std::move(tmpThread));
     }
 
     for (auto& th : threads) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 2929da4..68191bb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -925,6 +925,10 @@
     mkdir /data/system/dropbox 0700 system system
     mkdir /data/system/heapdump 0700 system system
     mkdir /data/system/users 0775 system system
+    # Mkdir and set SELinux security contexts for shutdown-checkpoints.
+    # TODO(b/270286197): remove these after couple releases.
+    mkdir /data/system/shutdown-checkpoints 0700 system system
+    restorecon_recursive /data/system/shutdown-checkpoints
 
     # Create the parent directories of the user CE and DE storage directories.
     # These parent directories must use encryption=None, since each of their
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index ca522b7..9a733bb 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -18,8 +18,11 @@
 in Marshmallow we changed direction and started the move to toybox.
 
 Not everything is provided by toybox, though. For the bzip2 command-line tools
-we use the ones that are part of the bzip2 distribution. The awk added in
-Android P is Brian Kernighan's "one true" awk.
+we use the ones that are part of the bzip2 distribution.
+The awk added in Android P is the
+["one true" awk](https://github.com/onetrueawk/awk).
+The bc added in Android Q is
+[Gavin Howard's bc](https://github.com/gavinhoward/bc).
 
 The lists below show what tools were provided and where they came from in
 each release starting with Gingerbread. This doesn't tell the full story,
@@ -34,6 +37,40 @@
 full list for a release by running `toybox` directly.
 
 
+## Android 14 ("U")
+
+BSD: fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+gavinhoward/bc: bc
+
+one-true-awk: awk
+
+toolbox: getevent getprop setprop start stop
+
+toybox ([0.8.9](http://landley.net/toybox/#10-01-2023)-ish):
+[ acpi base64 basename blkdiscard blkid blockdev **brctl** cal cat chattr
+chcon chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut
+date dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log **logger** logname losetup ls lsattr lsmod lsof lspci lsusb makedevs
+md5sum microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe
+more mount mountpoint mv nbd-client nc netcat netstat nice nl nohup
+nproc nsenter od partprobe paste patch pgrep pidof ping ping6 pivot\_root
+pkill pmap printenv printf prlimit ps pwd pwdx readelf readlink realpath
+renice restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee test time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl uclampset ulimit umount uname
+uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen
+vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
+
+
 ## Android 13 ("T")
 
 BSD: fsck\_msdos newfs\_msdos
@@ -46,7 +83,8 @@
 
 toolbox: getevent getprop setprop start stop
 
-toybox (0.8.6-ish): [ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
+toybox ([0.8.6](http://landley.net/toybox/#30-11-2021)-ish):
+[ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
 chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
 dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
 expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
@@ -79,7 +117,8 @@
 
 toolbox: getevent getprop setprop start stop
 
-toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
+toybox ([0.8.4](http://landley.net/toybox/#24-10-2020)-ish):
+**[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
 chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
 dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
 expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
@@ -112,7 +151,8 @@
 
 toolbox: getevent getprop setprop start stop
 
-toybox (0.8.3-ish): acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+toybox ([0.8.3](http://landley.net/toybox/#11-05-2020)-ish):
+acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
 chown chroot chrt cksum clear cmp comm cp cpio cut date dd **devmem**
 df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
 false fgrep file find flock fmt free freeramdisk fsfreeze **fsync** getconf
@@ -143,7 +183,8 @@
 
 toolbox: getevent getprop
 
-toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
+toybox ([0.8.0](http://landley.net/toybox/#08-02-2019)-ish):
+acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
 chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
 diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
 false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
@@ -174,7 +215,8 @@
 
 toolbox: getevent getprop newfs\_msdos
 
-toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox ([0.7.6](http://landley.net/toybox/#24-02-2018)-ish):
+acpi base64 basename blockdev cal cat chcon chgrp chmod chown
 chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
 dos2unix du echo env expand expr fallocate false file find flock **fmt** free
 getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
@@ -198,7 +240,8 @@
 
 toolbox: getevent newfs\_msdos
 
-toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox ([0.7.3](http://landley.net/toybox/#21-02-2017)-ish):
+acpi base64 basename blockdev cal cat chcon chgrp chmod chown
 chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
 dos2unix du echo env expand expr fallocate false **file** find flock free
 getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
@@ -221,7 +264,8 @@
 toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
 sendevent start stop top
 
-toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
+toybox ([0.7.0](http://landley.net/toybox/#02-02-2016)-ish):
+acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
 chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
 dos2unix **du** echo env expand expr fallocate false find **flock** free
 getenforce getprop groups head hostname hwclock id ifconfig inotifyd
@@ -242,7 +286,8 @@
 toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
 newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
 
-toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+toybox ([0.5.2](http://landley.net/toybox/#25-02-2015)-ish):
+acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
 chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
 env expand expr fallocate false find free getenforce getprop groups
 head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
diff --git a/trusty/keymaster/TEST_MAPPING b/trusty/keymaster/TEST_MAPPING
index 0dd39fb..0475e04 100644
--- a/trusty/keymaster/TEST_MAPPING
+++ b/trusty/keymaster/TEST_MAPPING
@@ -5,6 +5,18 @@
       },
       {
         "name": "VtsHalRemotelyProvisionedComponentTargetTest"
+      },
+      {
+        "name": "RkpdAppUnitTests"
+      },
+      {
+        "name": "RkpdAppGoogleUnitTests"
+      },
+      {
+        "name": "RkpdAppIntegrationTests"
+      },
+      {
+        "name": "RkpdAppGoogleIntegrationTests"
       }
   ]
 }