Merge "Lock down String8.isEmpty() usage" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5393e25..267571b 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -12,6 +12,7 @@
         "-Wno-unused-argument",
         "-Wno-unused-function",
         "-Wno-nullability-completeness",
+        "-Wno-reorder-init-list",
         "-Os",
         "-fno-finite-loops",
         "-DANDROID_DEBUGGABLE=0",
diff --git a/fastboot/README.md b/fastboot/README.md
index 55583eb..28e623c 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -188,6 +188,21 @@
     erase %s           Erase a given partition (can only be used in conjunction)
                        with if-wipe -> eg. if-wipe erase cache
 
+Flashing Optimization:
+
+    After generating the list of tasks to execute, Fastboot will try and
+    optimize the flashing of the dynamic partitions by constructing an
+    optimized flash super task. Fastboot will explicitly pattern match the
+    following commands and try and concatenate it into this task. (doing so
+    will allow us to avoid the reboot into userspace fastbootd which takes
+    significant time)
+
+    //Optimizable Block
+    reboot fastboot
+    update-super                        ---> generate optimized flash super task
+    $FOR EACH {dynamic partition}
+        flash {dynamic partition}
+
 ## Client Variables
 
 The "getvar:%s" command is used to read client variables which
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index d605393..6de598f 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -639,6 +639,12 @@
     return UpdateSuper(device, args[1], wipe);
 }
 
+static bool IsLockedDsu() {
+    std::string active_dsu;
+    android::gsi::GetActiveDsu(&active_dsu);
+    return android::base::EndsWith(active_dsu, ".lock");
+}
+
 bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() != 2) {
         return device->WriteFail("Invalid arguments");
@@ -653,6 +659,11 @@
         return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
     }
 
+    if ((args[1] == "wipe" || args[1] == "disable") && GetDeviceLockStatus() && IsLockedDsu()) {
+        // Block commands that modify the states of locked DSU
+        return device->WriteFail("Command not available on locked DSU/devices");
+    }
+
     if (args[1] == "wipe") {
         if (!android::gsi::UninstallGsi()) {
             return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index d7e3db6..21df729 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1286,7 +1286,7 @@
     return current_slot;
 }
 
-static int get_slot_count() {
+static int get_slot_count(fastboot::IFastBootDriver* fb) {
     std::string var;
     int count = 0;
     if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
@@ -1296,8 +1296,8 @@
     return count;
 }
 
-bool supports_AB() {
-    return get_slot_count() >= 2;
+bool supports_AB(fastboot::IFastBootDriver* fb) {
+    return get_slot_count(fb) >= 2;
 }
 
 // Given a current slot, this returns what the 'other' slot is.
@@ -1309,7 +1309,7 @@
 }
 
 static std::string get_other_slot(const std::string& current_slot) {
-    return get_other_slot(current_slot, get_slot_count());
+    return get_other_slot(current_slot, get_slot_count(fb));
 }
 
 static std::string get_other_slot(int count) {
@@ -1317,7 +1317,7 @@
 }
 
 static std::string get_other_slot() {
-    return get_other_slot(get_current_slot(), get_slot_count());
+    return get_other_slot(get_current_slot(), get_slot_count(fb));
 }
 
 static std::string verify_slot(const std::string& slot_name, bool allow_all) {
@@ -1326,7 +1326,7 @@
         if (allow_all) {
             return "all";
         } else {
-            int count = get_slot_count();
+            int count = get_slot_count(fb);
             if (count > 0) {
                 return "a";
             } else {
@@ -1335,7 +1335,7 @@
         }
     }
 
-    int count = get_slot_count();
+    int count = get_slot_count(fb);
     if (count == 0) die("Device does not support slots");
 
     if (slot == "other") {
@@ -1408,7 +1408,7 @@
                 slot.c_str());
         }
         if (has_slot == "yes") {
-            for (int i = 0; i < get_slot_count(); i++) {
+            for (int i = 0; i < get_slot_count(fb); i++) {
                 do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
             }
         } else {
@@ -1529,7 +1529,7 @@
 // Sets slot_override as the active slot. If slot_override is blank,
 // set current slot as active instead. This clears slot-unbootable.
 static void set_active(const std::string& slot_override) {
-    if (!supports_AB()) return;
+    if (!supports_AB(fb)) return;
 
     if (slot_override != "") {
         fb->SetActive(slot_override);
@@ -1844,7 +1844,7 @@
         fp_->secondary_slot = get_other_slot();
     }
     if (fp_->secondary_slot == "") {
-        if (supports_AB()) {
+        if (supports_AB(fb)) {
             fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
         }
         fp_->skip_secondary = true;
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 75b8d29..35deea7 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -29,10 +29,8 @@
 
 #include <functional>
 #include <string>
-#include "fastboot_driver.h"
 #include "fastboot_driver_interface.h"
 #include "filesystem.h"
-#include "super_flash_helper.h"
 #include "task.h"
 #include "util.h"
 
@@ -183,12 +181,12 @@
 };
 
 Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
-bool supports_AB();
 std::string GetPartitionName(const ImageEntry& entry, const 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, const FlashingPlan* fp);
 std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
 
+bool supports_AB(fastboot::IFastBootDriver* fb);
 bool is_retrofit_device(fastboot::IFastBootDriver* fb);
 bool is_logical(const std::string& partition);
 void fb_perform_format(const std::string& partition, int skip_if_not_supported,
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 30298cb..49cebd9 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -27,7 +27,6 @@
  */
 #pragma once
 #include <cstdlib>
-#include <deque>
 #include <functional>
 #include <limits>
 #include <memory>
@@ -38,10 +37,8 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <bootimg.h>
-#include <inttypes.h>
 #include <sparse/sparse.h>
 
-#include "constants.h"
 #include "fastboot_driver_interface.h"
 #include "transport.h"
 
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 43aaec9..79f3939 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -50,6 +50,7 @@
 #include <gtest/gtest.h>
 #include <sparse/sparse.h>
 
+#include "constants.h"
 #include "fastboot_driver.h"
 #include "usb.h"
 
@@ -928,8 +929,7 @@
 
     ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
     std::string resp;
-    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
-                << "Device is unresponsive to getvar command";
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "Device is unresponsive to getvar command";
 }
 
 TEST_F(Fuzz, CommandTooLarge) {
@@ -985,11 +985,10 @@
 TEST_F(Fuzz, SparseZeroBlkSize) {
     // handcrafted malform sparse file with zero as block size
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1004,13 +1003,10 @@
 TEST_F(Fuzz, SparseVeryLargeBlkSize) {
     // handcrafted sparse file with block size of ~4GB and divisible 4
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
-        '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
-        '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
-        '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
-        '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\xF0', '\xFF', '\xFF', '\xFF', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1021,11 +1017,10 @@
 TEST_F(Fuzz, SparseTrimmed) {
     // handcrafted malform sparse file which is trimmed
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1040,11 +1035,10 @@
 TEST_F(Fuzz, SparseInvalidChurk) {
     // handcrafted malform sparse file with invalid churk
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1894,7 +1888,8 @@
     if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
         printf("<Waiting for Device>\n");
         const auto matcher = [](usb_ifc_info* info) -> int {
-            return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+            return fastboot::FastBootTest::MatchFastboot(info,
+                                                         fastboot::FastBootTest::device_serial);
         };
         std::unique_ptr<Transport> transport;
         while (!transport) {
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index f0eed0c..f13dd55 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -15,8 +15,7 @@
 //
 #include "task.h"
 
-#include <cstddef>
-#include <iostream>
+#include "fastboot_driver.h"
 
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -130,6 +129,7 @@
     // Send the data to the device.
     flash_partition_files(super_name_, files);
 }
+
 std::string OptimizedFlashSuperTask::ToString() const {
     return "optimized-flash-super";
 }
@@ -165,7 +165,7 @@
         LOG(INFO) << "super optimization is disabled";
         return nullptr;
     }
-    if (!supports_AB()) {
+    if (!supports_AB(fp->fb)) {
         LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
         return nullptr;
     }
@@ -218,17 +218,21 @@
 
     auto s = helper->GetSparseLayout();
     if (!s) return nullptr;
-    // Remove images that we already flashed, just in case we have non-dynamic OS images.
+
+    // Remove tasks that are concatenated into this optimized task
     auto remove_if_callback = [&](const auto& task) -> bool {
         if (auto flash_task = task->AsFlashTask()) {
             return helper->WillFlash(flash_task->GetPartitionAndSlot());
         } else if (auto update_super_task = task->AsUpdateSuperTask()) {
             return true;
         } else if (auto reboot_task = task->AsRebootTask()) {
-            return true;
+            if (reboot_task->GetTarget() == "fastboot") {
+                return true;
+            }
         }
         return false;
     };
+
     tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
 
     return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
diff --git a/fastboot/task.h b/fastboot/task.h
index 6ebe381..a98c874 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -15,10 +15,8 @@
 //
 #pragma once
 
-#include <sstream>
 #include <string>
 
-#include "fastboot_driver.h"
 #include "super_flash_helper.h"
 #include "util.h"
 
@@ -29,6 +27,7 @@
 class FlashTask;
 class RebootTask;
 class UpdateSuperTask;
+class OptimizedFlashSuperTask;
 class WipeTask;
 class ResizeTask;
 class Task {
@@ -40,6 +39,7 @@
     virtual FlashTask* AsFlashTask() { return nullptr; }
     virtual RebootTask* AsRebootTask() { return nullptr; }
     virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
+    virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() { return nullptr; }
     virtual WipeTask* AsWipeTask() { return nullptr; }
     virtual ResizeTask* AsResizeTask() { return nullptr; }
 
@@ -86,13 +86,13 @@
   public:
     OptimizedFlashSuperTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
                             SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);
+    virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() override { return this; }
 
     static std::unique_ptr<OptimizedFlashSuperTask> Initialize(
             const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
     static bool CanOptimize(const ImageSource* source,
                             const std::vector<std::unique_ptr<Task>>& tasks);
 
-    using ImageEntry = std::pair<const Image*, std::string>;
     void Run() override;
     std::string ToString() const override;
 
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 1ba3f4a..1e25b6f 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -19,11 +19,10 @@
 #include "fastboot_driver_mock.h"
 
 #include <gtest/gtest.h>
-#include <fstream>
 #include <iostream>
 #include <memory>
-#include <unordered_map>
 #include "android-base/strings.h"
+#include "gmock/gmock.h"
 
 using android::base::Split;
 using testing::_;
@@ -235,3 +234,141 @@
             << "size of fastboot-info task list: " << fastboot_info_tasks.size()
             << " size of hardcoded task list: " << hardcoded_tasks.size();
 }
+TEST_F(ParseTest, IsDynamicParitiontest) {
+    if (!get_android_product_out()) {
+        GTEST_SKIP();
+    }
+
+    LocalImageSource s;
+    fp->source = &s;
+
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+    fp->should_optimize_flash_super = true;
+    fp->should_use_fastboot_info = true;
+
+    std::vector<std::pair<std::string, bool>> test_cases = {
+            {"flash boot", false},
+            {"flash init_boot", false},
+            {"flash --apply-vbmeta vbmeta", false},
+            {"flash product", true},
+            {"flash system", true},
+            {"flash --slot-other system system_other.img", true},
+    };
+    for (auto& test : test_cases) {
+        std::unique_ptr<Task> task =
+                ParseFastbootInfoLine(fp.get(), android::base::Tokenize(test.first, " "));
+        auto flash_task = task->AsFlashTask();
+        ASSERT_FALSE(flash_task == nullptr);
+        ASSERT_EQ(FlashTask::IsDynamicParitition(fp->source, flash_task), test.second);
+    }
+}
+
+TEST_F(ParseTest, CanOptimizeTest) {
+    if (!get_android_product_out()) {
+        GTEST_SKIP();
+    }
+
+    LocalImageSource s;
+    fp->source = &s;
+    fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+    fp->should_optimize_flash_super = false;
+    fp->should_use_fastboot_info = true;
+
+    std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             false},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+              "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+             false},
+    };
+
+    auto remove_if_callback = [&](const auto& task) -> bool { return !!task->AsResizeTask(); };
+
+    for (auto& test : patternmatchtest) {
+        std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+        tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
+        ASSERT_EQ(OptimizedFlashSuperTask::CanOptimize(fp->source, tasks), test.second);
+    }
+}
+
+// Note: this test is exclusively testing that optimized flash super pattern matches a given task
+// list and is able to optimized based on a correct sequence of tasks
+TEST_F(ParseTest, OptimizedFlashSuperPatternMatchTest) {
+    if (!get_android_product_out()) {
+        GTEST_SKIP();
+    }
+
+    LocalImageSource s;
+    fp->source = &s;
+    fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+    fp->should_optimize_flash_super = true;
+    fp->should_use_fastboot_info = true;
+
+    ON_CALL(fb, GetVar("super-partition-name", _, _))
+            .WillByDefault(testing::Return(fastboot::BAD_ARG));
+
+    ON_CALL(fb, GetVar("slot-count", _, _))
+            .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("2"),
+                                          testing::Return(fastboot::SUCCESS)));
+
+    ON_CALL(fb, GetVar("partition-size:super", _, _))
+            .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("1000"),
+                                          testing::Return(fastboot::SUCCESS)));
+
+    std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             false},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+              "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+             false},
+    };
+
+    for (auto& test : patternmatchtest) {
+        std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+        // Check to make sure we have an optimized flash super task && no more dynamic partition
+        // flashing tasks
+        auto&& IsOptimized = [](const FlashingPlan* fp,
+                                const std::vector<std::unique_ptr<Task>>& tasks) {
+            bool contains_optimized_task = false;
+            for (auto& task : tasks) {
+                if (auto optimized_task = task->AsOptimizedFlashSuperTask()) {
+                    contains_optimized_task = true;
+                }
+                if (auto flash_task = task->AsFlashTask()) {
+                    if (FlashTask::IsDynamicParitition(fp->source, flash_task)) {
+                        return false;
+                    }
+                }
+            }
+            return contains_optimized_task;
+        };
+        ASSERT_EQ(IsOptimized(fp.get(), tasks), test.second);
+    }
+}
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 68576f2..2cc0d2d 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -688,7 +688,7 @@
                 continue;
             }
         } else {
-            if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
+            if (!fs_mgr_overlayfs_already_mounted(overlay_mount_point, false /* overlay */)) {
                 continue;
             }
         }
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index 8fb63b1..9f17c06 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -733,15 +733,18 @@
 
 bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
     Fstab fstab;
-    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+    if (!ReadFstabFromProcMounts(&fstab)) {
         return false;
     }
     const auto lowerdir = kLowerdirOption + mount_point;
-    for (const auto& entry : fstab) {
-        if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
-        if (mount_point != entry.mount_point) continue;
-        if (!overlay_only) return true;
-        const auto options = android::base::Split(entry.fs_options, ",");
+    for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
+        if (!overlay_only) {
+            return true;
+        }
+        if (entry->fs_type != "overlay" && entry->fs_type != "overlayfs") {
+            continue;
+        }
+        const auto options = android::base::Split(entry->fs_options, ",");
         for (const auto& opt : options) {
             if (opt == lowerdir) {
                 return true;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index b0f2b84..0131f73 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -476,6 +476,22 @@
     header_libs: [
         "libstorage_literals_headers",
     ],
+
+    dist: {
+        targets: [
+            "sdk",
+            "sdk-repo-platform-tools",
+            "sdk_repo",
+        ],
+    },
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
 }
 
 python_library_host {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
index cf65615..fd12b84 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -24,24 +24,26 @@
 
 class ICompressor {
   public:
-    explicit ICompressor(uint32_t compression_level) : compression_level_(compression_level) {}
+    explicit ICompressor(uint32_t compression_level, uint32_t block_size)
+        : compression_level_(compression_level), block_size_(block_size) {}
 
     virtual ~ICompressor() {}
     // Factory methods for compression methods.
-    static std::unique_ptr<ICompressor> Gz(uint32_t compression_level);
-    static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level);
-    static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level);
+    static std::unique_ptr<ICompressor> Gz(uint32_t compression_level, const int32_t BLOCK_SZ);
+    static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level, const int32_t BLOCK_SZ);
+    static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level, const int32_t BLOCK_SZ);
     static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t BLOCK_SZ);
 
     static std::unique_ptr<ICompressor> Create(CowCompression compression, const int32_t BLOCK_SZ);
 
     uint32_t GetCompressionLevel() const { return compression_level_; }
-
+    uint32_t GetBlockSize() const { return block_size_; }
     [[nodiscard]] virtual std::basic_string<uint8_t> Compress(const void* data,
                                                               size_t length) const = 0;
 
   private:
     uint32_t compression_level_;
+    uint32_t block_size_;
 };
 }  // namespace snapshot
 }  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 6bc8b9b..e7b0020 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -73,6 +73,9 @@
 class SnapshotMergeStats;
 class SnapshotStatus;
 
+using std::chrono::duration_cast;
+using namespace std::chrono_literals;
+
 static constexpr const std::string_view kCowGroupName = "cow";
 static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
 
@@ -424,6 +427,7 @@
     FRIEND_TEST(SnapshotTest, MergeFailureCode);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+    FRIEND_TEST(SnapshotTest, BootSnapshotWithoutSlotSwitch);
     FRIEND_TEST(SnapshotUpdateTest, AddPartition);
     FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
     FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
@@ -436,6 +440,7 @@
     FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
+    FRIEND_TEST(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch);
     friend class SnapshotTest;
     friend class SnapshotUpdateTest;
     friend class FlashAfterUpdateTest;
@@ -456,7 +461,7 @@
     bool EnsureImageManager();
 
     // Ensure we're connected to snapuserd.
-    bool EnsureSnapuserdConnected();
+    bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);
 
     // Helpers for first-stage init.
     const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
@@ -549,6 +554,16 @@
     // Unmap and remove all known snapshots.
     bool RemoveAllSnapshots(LockedFile* lock);
 
+    // Boot device off snapshots without slot switch
+    bool BootFromSnapshotsWithoutSlotSwitch();
+
+    // Remove kBootSnapshotsWithoutSlotSwitch so that device can boot
+    // without snapshots on the current slot
+    bool PrepareDeviceToBootWithoutSnapshot();
+
+    // Is the kBootSnapshotsWithoutSlotSwitch present
+    bool IsSnapshotWithoutSlotSwitch();
+
     // List the known snapshot names.
     bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
                        const std::string& suffix = "");
@@ -663,6 +678,7 @@
     std::string GetRollbackIndicatorPath();
     std::string GetForwardMergeIndicatorPath();
     std::string GetOldPartitionMetadataPath();
+    std::string GetBootSnapshotsWithoutSlotSwitchPath();
 
     const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index 71ac59f..abc7d33 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -56,16 +56,16 @@
 }
 
 std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
-                                                 const int32_t BLOCK_SZ) {
+                                                 const int32_t block_size) {
     switch (compression.algorithm) {
         case kCowCompressLz4:
-            return ICompressor::Lz4(compression.compression_level);
+            return ICompressor::Lz4(compression.compression_level, block_size);
         case kCowCompressBrotli:
-            return ICompressor::Brotli(compression.compression_level);
+            return ICompressor::Brotli(compression.compression_level, block_size);
         case kCowCompressGz:
-            return ICompressor::Gz(compression.compression_level);
+            return ICompressor::Gz(compression.compression_level, block_size);
         case kCowCompressZstd:
-            return ICompressor::Zstd(compression.compression_level, BLOCK_SZ);
+            return ICompressor::Zstd(compression.compression_level, block_size);
         case kCowCompressNone:
             return nullptr;
     }
@@ -100,7 +100,8 @@
 
 class GzCompressor final : public ICompressor {
   public:
-    GzCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+    GzCompressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size){};
 
     std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
         const auto bound = compressBound(length);
@@ -120,7 +121,8 @@
 
 class Lz4Compressor final : public ICompressor {
   public:
-    Lz4Compressor(uint32_t compression_level) : ICompressor(compression_level){};
+    Lz4Compressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size){};
 
     std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
         const auto bound = LZ4_compressBound(length);
@@ -151,7 +153,8 @@
 
 class BrotliCompressor final : public ICompressor {
   public:
-    BrotliCompressor(uint32_t compression_level) : ICompressor(compression_level){};
+    BrotliCompressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size){};
 
     std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
         const auto bound = BrotliEncoderMaxCompressedSize(length);
@@ -176,10 +179,11 @@
 
 class ZstdCompressor final : public ICompressor {
   public:
-    ZstdCompressor(uint32_t compression_level, const uint32_t MAX_BLOCK_SIZE)
-        : ICompressor(compression_level), zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
+    ZstdCompressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size),
+          zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
         ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
-        ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(MAX_BLOCK_SIZE));
+        ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(GetBlockSize()));
     };
 
     std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
@@ -313,20 +317,23 @@
     return true;
 }
 
-std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level) {
-    return std::make_unique<BrotliCompressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level,
+                                                 const int32_t block_size) {
+    return std::make_unique<BrotliCompressor>(compression_level, block_size);
 }
 
-std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level) {
-    return std::make_unique<GzCompressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level, const int32_t block_size) {
+    return std::make_unique<GzCompressor>(compression_level, block_size);
 }
 
-std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level) {
-    return std::make_unique<Lz4Compressor>(compression_level);
+std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level,
+                                              const int32_t block_size) {
+    return std::make_unique<Lz4Compressor>(compression_level, block_size);
 }
 
-std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level, const int32_t BLOCK_SZ) {
-    return std::make_unique<ZstdCompressor>(compression_level, BLOCK_SZ);
+std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level,
+                                               const int32_t block_size) {
+    return std::make_unique<ZstdCompressor>(compression_level, block_size);
 }
 
 void CompressWorker::Finalize() {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 51389a0..4743a42 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -83,6 +83,8 @@
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+        "/metadata/ota/snapshot-boot-without-slot-switch";
 static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
 static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
 static constexpr auto kUpdateStateCheckInterval = 2s;
@@ -217,6 +219,12 @@
     auto file = LockExclusive();
     if (!file) return false;
 
+    if (IsSnapshotWithoutSlotSwitch()) {
+        LOG(ERROR) << "Cannot cancel the snapshots as partitions are mounted off the snapshots on "
+                      "current slot.";
+        return false;
+    }
+
     UpdateState state = ReadUpdateState(file.get());
     if (state == UpdateState::None) {
         RemoveInvalidSnapshots(file.get());
@@ -299,10 +307,9 @@
     // - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
     // matches the incoming update.
     std::vector<std::string> files = {
-            GetSnapshotBootIndicatorPath(),
-            GetRollbackIndicatorPath(),
-            GetForwardMergeIndicatorPath(),
-            GetOldPartitionMetadataPath(),
+            GetSnapshotBootIndicatorPath(),          GetRollbackIndicatorPath(),
+            GetForwardMergeIndicatorPath(),          GetOldPartitionMetadataPath(),
+            GetBootSnapshotsWithoutSlotSwitchPath(),
     };
     for (const auto& file : files) {
         RemoveFileIfExists(file);
@@ -483,6 +490,32 @@
             LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
             return false;
         }
+    } else if (IsSnapshotWithoutSlotSwitch()) {
+        // When snapshots are on current slot, we determine the size
+        // of block device based on the number of COW operations. We cannot
+        // use base device as it will be from older image.
+        size_t num_ops = 0;
+        uint64_t dev_sz = 0;
+        unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "Failed to open " << cow_file;
+            return false;
+        }
+
+        CowReader reader;
+        if (!reader.Parse(std::move(fd))) {
+            LOG(ERROR) << "Failed to parse cow " << cow_file;
+            return false;
+        }
+
+        const auto& header = reader.GetHeader();
+        if (header.prefix.major_version > 2) {
+            LOG(ERROR) << "COW format not supported";
+            return false;
+        }
+        num_ops = reader.get_num_total_data_ops();
+        dev_sz = (num_ops * header.block_size);
+        base_sectors = dev_sz >> 9;
     } else {
         // For userspace snapshots, the size of the base device is taken as the
         // size of the dm-user block device. Since there is no pseudo mapping
@@ -1479,6 +1512,10 @@
     return result;
 }
 
+std::string SnapshotManager::GetBootSnapshotsWithoutSlotSwitchPath() {
+    return metadata_dir_ + "/" + android::base::Basename(kBootSnapshotsWithoutSlotSwitch);
+}
+
 std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
     return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
 }
@@ -2120,6 +2157,10 @@
     return state;
 }
 
+bool SnapshotManager::IsSnapshotWithoutSlotSwitch() {
+    return (access(GetBootSnapshotsWithoutSlotSwitchPath().c_str(), F_OK) == 0);
+}
+
 bool SnapshotManager::UpdateUsesCompression() {
     auto lock = LockShared();
     if (!lock) return false;
@@ -2212,6 +2253,13 @@
 }
 
 bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+    if (IsSnapshotWithoutSlotSwitch()) {
+        if (GetCurrentSlot() != Slot::Source) {
+            LOG(ERROR) << "Snapshots marked to boot without slot switch; but slot is wrong";
+            return false;
+        }
+        return true;
+    }
     // If we fail to read, we'll wind up using CreateLogicalPartitions, which
     // will create devices that look like the old slot, except with extra
     // content at the end of each device. This will confuse dm-verity, and
@@ -2347,7 +2395,8 @@
     // completed, live_snapshot_status is set to nullopt.
     std::optional<SnapshotStatus> live_snapshot_status;
     do {
-        if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
+        if (!IsSnapshotWithoutSlotSwitch() &&
+            !(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
             LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
                       << params.GetPartitionName();
             break;
@@ -2703,7 +2752,7 @@
     // to unmap; hence, we can't be deleting the device
     // as the table would be mounted off partitions and will fail.
     if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {
-        if (!DeleteDeviceIfExists(dm_user_name)) {
+        if (!DeleteDeviceIfExists(dm_user_name, 4000ms)) {
             LOG(ERROR) << "Cannot unmap " << dm_user_name;
             return false;
         }
@@ -3098,7 +3147,7 @@
     return true;
 }
 
-bool SnapshotManager::EnsureSnapuserdConnected() {
+bool SnapshotManager::EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms) {
     if (snapuserd_client_) {
         return true;
     }
@@ -3107,7 +3156,7 @@
         return false;
     }
 
-    snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+    snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, timeout_ms);
     if (!snapuserd_client_) {
         LOG(ERROR) << "Unable to connect to snapuserd";
         return false;
@@ -4372,13 +4421,70 @@
 bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
     auto slot = GetCurrentSlot();
     if (slot == Slot::Target) {
+        // Merge in-progress
         if (IsSnapuserdRequired()) {
             return true;
         }
     }
 
+    // Let's check more deeper to see if snapshots are mounted
+    auto lock = LockExclusive();
+    if (!lock) {
+        return false;
+    }
+
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        return false;
+    }
+
+    for (const auto& snapshot : snapshots) {
+        // Active snapshot and daemon is alive
+        if (IsSnapshotDevice(snapshot) && EnsureSnapuserdConnected(2s)) {
+            return true;
+        }
+    }
+
     return false;
 }
 
+bool SnapshotManager::BootFromSnapshotsWithoutSlotSwitch() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    auto contents = device_->GetSlotSuffix();
+    // This is the indicator which tells first-stage init
+    // to boot from snapshots even though there was no slot-switch
+    auto boot_file = GetBootSnapshotsWithoutSlotSwitchPath();
+    if (!WriteStringToFileAtomic(contents, boot_file)) {
+        PLOG(ERROR) << "write failed: " << boot_file;
+        return false;
+    }
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    update_status.set_state(UpdateState::Initiated);
+    update_status.set_userspace_snapshots(true);
+    update_status.set_using_snapuserd(true);
+    if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::PrepareDeviceToBootWithoutSnapshot() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    android::base::RemoveFileIfExists(GetSnapshotBootIndicatorPath());
+    android::base::RemoveFileIfExists(GetBootSnapshotsWithoutSlotSwitchPath());
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    update_status.set_state(UpdateState::Cancelled);
+    if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+        return false;
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 3b6d26a..e506110 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2559,6 +2559,56 @@
     }
 }
 
+TEST_F(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch) {
+    MountMetadata();
+    AddOperationForPartitions();
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    if (!sm->UpdateUsesUserSnapshots()) {
+        GTEST_SKIP() << "Test does not apply as UserSnapshots aren't enabled.";
+    }
+
+    ASSERT_TRUE(WriteSnapshots());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+    if (ShouldSkipLegacyMerging()) {
+        GTEST_SKIP() << "Skipping legacy merge test";
+    }
+    // Mark the indicator
+    ASSERT_TRUE(sm->BootFromSnapshotsWithoutSlotSwitch());
+
+    ASSERT_TRUE(sm->EnsureSnapuserdConnected());
+    sm->set_use_first_stage_snapuserd(true);
+
+    ASSERT_TRUE(sm->NeedSnapshotsInFirstStageMount());
+
+    // Map snapshots
+    ASSERT_TRUE(sm->MapAllSnapshots(10s));
+
+    // New updates should fail
+    ASSERT_FALSE(sm->BeginUpdate());
+
+    // Snapshots cannot be cancelled
+    ASSERT_FALSE(sm->CancelUpdate());
+
+    // Merge cannot start
+    ASSERT_FALSE(sm->InitiateMerge());
+
+    // Read bytes back and verify they match the cache.
+    ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+
+    // Remove the indicators
+    ASSERT_TRUE(sm->PrepareDeviceToBootWithoutSnapshot());
+
+    // Ensure snapshots are still mounted
+    ASSERT_TRUE(sm->IsUserspaceSnapshotUpdateInProgress());
+
+    // Cleanup snapshots
+    ASSERT_TRUE(sm->UnmapAllSnapshots());
+}
+
 TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
     AddOperationForPartitions();
     // Execute the update.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 1323b0b..ebaca2d 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -75,7 +75,11 @@
                  "  unmap-snapshots\n"
                  "    Unmap all pre-created snapshots\n"
                  "  delete-snapshots\n"
-                 "    Delete all pre-created snapshots\n";
+                 "    Delete all pre-created snapshots\n"
+                 "  revert-snapshots\n"
+                 "    Prepares devices to boot without snapshots on next boot.\n"
+                 "    This does not delete the snapshot. It only removes the indicators\n"
+                 "    so that first stage init will not mount from snapshots.\n";
     return EX_USAGE;
 }
 
@@ -87,9 +91,11 @@
     MapSnapshots(std::string path = "");
     bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
     bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
-    bool WaitForSnapshotWritesToComplete();
+    bool FinishSnapshotWrites();
     bool UnmapCowImagePath(std::string& name);
-    bool DeleteCowImage(std::string& name);
+    bool DeleteSnapshots();
+    bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
+    bool BeginUpdate();
 
   private:
     std::optional<std::string> GetCowImagePath(std::string& name);
@@ -107,7 +113,24 @@
         exit(1);
     }
     snapshot_dir_path_ = path + "/";
+}
+
+bool MapSnapshots::BeginUpdate() {
     lock_ = sm_->LockExclusive();
+    std::vector<std::string> snapshots;
+    sm_->ListSnapshots(lock_.get(), &snapshots);
+    if (!snapshots.empty()) {
+        // Snapshots are already present.
+        return true;
+    }
+
+    lock_ = nullptr;
+    if (!sm_->BeginUpdate()) {
+        LOG(ERROR) << "BeginUpdate failed";
+        return false;
+    }
+    lock_ = sm_->LockExclusive();
+    return true;
 }
 
 bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
@@ -130,6 +153,9 @@
     dev_sz &= ~(block_sz - 1);
 
     SnapshotStatus status;
+    status.set_state(SnapshotState::CREATED);
+    status.set_using_snapuserd(true);
+    status.set_old_partition_size(0);
     status.set_name(partition_name);
     status.set_cow_file_size(dev_sz);
     status.set_cow_partition_size(0);
@@ -216,27 +242,33 @@
     return true;
 }
 
-bool MapSnapshots::WaitForSnapshotWritesToComplete() {
+bool MapSnapshots::FinishSnapshotWrites() {
     bool ret = true;
     for (auto& t : threads_) {
         ret = t.get() && ret;
     }
 
+    lock_ = nullptr;
     if (ret) {
         LOG(INFO) << "Pre-created snapshots successfully copied";
-    } else {
-        LOG(ERROR) << "Snapshot copy failed";
+        if (!sm_->FinishedSnapshotWrites(false)) {
+            return false;
+        }
+        return sm_->BootFromSnapshotsWithoutSlotSwitch();
     }
-    return ret;
+
+    LOG(ERROR) << "Snapshot copy failed";
+    return false;
 }
 
 bool MapSnapshots::UnmapCowImagePath(std::string& name) {
     return sm_->UnmapCowImage(name);
 }
 
-bool MapSnapshots::DeleteCowImage(std::string& name) {
-    if (!sm_->DeleteSnapshot(lock_.get(), name)) {
-        LOG(ERROR) << "Delete snapshot failed";
+bool MapSnapshots::DeleteSnapshots() {
+    lock_ = sm_->LockExclusive();
+    if (!sm_->RemoveAllUpdateState(lock_.get())) {
+        LOG(ERROR) << "Remove All Update State failed";
         return false;
     }
     return true;
@@ -281,7 +313,8 @@
     return true;
 }
 
-bool UnMapPrecreatedSnapshots(int, char**) {
+bool UnMapPrecreatedSnapshots(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
     // Make sure we are root.
     if (::getuid() != 0) {
         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
@@ -302,29 +335,36 @@
     return true;
 }
 
-bool DeletePrecreatedSnapshots(int, char**) {
+bool RemovePrecreatedSnapshots(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+        return false;
+    }
+
+    MapSnapshots snapshot;
+    if (!snapshot.CleanupSnapshot()) {
+        LOG(ERROR) << "CleanupSnapshot failed";
+        return false;
+    }
+    return true;
+}
+
+bool DeletePrecreatedSnapshots(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
     // Make sure we are root.
     if (::getuid() != 0) {
         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
         return EXIT_FAILURE;
     }
 
-    std::vector<std::string> partitions;
-    if (!GetVerityPartitions(partitions)) {
-        return false;
-    }
-
     MapSnapshots snapshot;
-    for (auto partition : partitions) {
-        if (!snapshot.DeleteCowImage(partition)) {
-            LOG(ERROR) << "DeleteCowImage failed: " << partition;
-        }
-    }
-    return true;
+    return snapshot.DeleteSnapshots();
 }
 
 bool MapPrecreatedSnapshots(int argc, char** argv) {
-    android::base::InitLogging(argv, &android::base::StderrLogger);
+    android::base::InitLogging(argv, &android::base::KernelLogger);
 
     // Make sure we are root.
     if (::getuid() != 0) {
@@ -365,6 +405,11 @@
     }
 
     MapSnapshots cow(path);
+    if (!cow.BeginUpdate()) {
+        LOG(ERROR) << "BeginUpdate failed";
+        return false;
+    }
+
     for (auto& pair : partitions) {
         if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
             LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
@@ -376,7 +421,7 @@
         }
     }
 
-    return cow.WaitForSnapshotWritesToComplete();
+    return cow.FinishSnapshotWrites();
 }
 
 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
@@ -508,6 +553,7 @@
         {"map-snapshots", MapPrecreatedSnapshots},
         {"unmap-snapshots", UnMapPrecreatedSnapshots},
         {"delete-snapshots", DeletePrecreatedSnapshots},
+        {"revert-snapshots", RemovePrecreatedSnapshots},
         // clang-format on
 };
 
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 2b72847..4301f0e 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -430,31 +430,6 @@
 // ---------------------------------------------------------------------------
 // Path functions
 
-static void setPathName(String8& s, const char* name) {
-    size_t len = strlen(name);
-    char* buf = s.lockBuffer(len);
-
-    memcpy(buf, name, len);
-
-    // remove trailing path separator, if present
-    if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
-    buf[len] = '\0';
-
-    s.unlockBuffer(len);
-}
-
-String8 String8::getPathLeaf(void) const
-{
-    const char* cp;
-    const char*const buf = mString;
-
-    cp = strrchr(buf, OS_PATH_SEPARATOR);
-    if (cp == nullptr)
-        return String8(*this);
-    else
-        return String8(cp+1);
-}
-
 String8 String8::getPathDir(void) const
 {
     const char* cp;
@@ -467,40 +442,14 @@
         return String8(str, cp - str);
 }
 
-String8 String8::walkPath(String8* outRemains) const
-{
-    const char* cp;
-    const char*const str = mString;
-    const char* buf = str;
-
-    cp = strchr(buf, OS_PATH_SEPARATOR);
-    if (cp == buf) {
-        // don't include a leading '/'.
-        buf = buf+1;
-        cp = strchr(buf, OS_PATH_SEPARATOR);
-    }
-
-    if (cp == nullptr) {
-        String8 res = buf != str ? String8(buf) : *this;
-        if (outRemains) *outRemains = String8("");
-        return res;
-    }
-
-    String8 res(buf, cp-buf);
-    if (outRemains) *outRemains = String8(cp+1);
-    return res;
-}
-
 /*
  * Helper function for finding the start of an extension in a pathname.
  *
  * Returns a pointer inside mString, or NULL if no extension was found.
  */
-char* String8::find_extension(void) const
-{
+static const char* find_extension(const char* str) {
     const char* lastSlash;
     const char* lastDot;
-    const char* const str = mString;
 
     // only look at the filename
     lastSlash = strrchr(str, OS_PATH_SEPARATOR);
@@ -515,67 +464,16 @@
         return nullptr;
 
     // looks good, ship it
-    return const_cast<char*>(lastDot);
+    return lastDot;
 }
 
 String8 String8::getPathExtension(void) const
 {
-    char* ext;
-
-    ext = find_extension();
+    auto ext = find_extension(mString);
     if (ext != nullptr)
         return String8(ext);
     else
         return String8("");
 }
 
-String8 String8::getBasePath(void) const
-{
-    char* ext;
-    const char* const str = mString;
-
-    ext = find_extension();
-    if (ext == nullptr)
-        return String8(*this);
-    else
-        return String8(str, ext - str);
-}
-
-String8& String8::appendPath(const char* name)
-{
-    // TODO: The test below will fail for Win32 paths. Fix later or ignore.
-    if (name[0] != OS_PATH_SEPARATOR) {
-        if (*name == '\0') {
-            // nothing to do
-            return *this;
-        }
-
-        size_t len = length();
-        if (len == 0) {
-            // no existing filename, just use the new one
-            setPathName(*this, name);
-            return *this;
-        }
-
-        // make room for oldPath + '/' + newPath
-        int newlen = strlen(name);
-
-        char* buf = lockBuffer(len+1+newlen);
-
-        // insert a '/' if needed
-        if (buf[len-1] != OS_PATH_SEPARATOR)
-            buf[len++] = OS_PATH_SEPARATOR;
-
-        memcpy(buf+len, name, newlen+1);
-        len += newlen;
-
-        unlockBuffer(len);
-
-        return *this;
-    } else {
-        setPathName(*this, name);
-        return *this;
-    }
-}
-
 }; // namespace android
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index 6f7a54f..cbce050 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -68,30 +68,6 @@
                     int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
                     str1->find(str2->c_str(), start_index);
                 },
-
-                // Path handling
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getBasePath();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getPathExtension();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getPathLeaf();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getPathDir();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    std::shared_ptr<android::String8> path_out_str =
-                            std::make_shared<android::String8>();
-                    str1->walkPath(path_out_str.get());
-                    path_out_str->clear();
-                },
-                [](FuzzedDataProvider* dataProvider, android::String8* str1,
-                   android::String8*) -> void {
-                    str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
-                },
 };
 
 void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
diff --git a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
index 46badde..8881b44 100644
--- a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
    "name" : "_ZN7android7RefBaseD2Ev"
   },
   {
-   "name" : "_ZN7android7String810appendPathEPKc"
-  },
-  {
    "name" : "_ZN7android7String810lockBufferEm"
   },
   {
@@ -1145,15 +1142,6 @@
    "name" : "_ZNK7android7String810getPathDirEv"
   },
   {
-   "name" : "_ZNK7android7String811getBasePathEv"
-  },
-  {
-   "name" : "_ZNK7android7String811getPathLeafEv"
-  },
-  {
-   "name" : "_ZNK7android7String814find_extensionEv"
-  },
-  {
    "name" : "_ZNK7android7String816getPathExtensionEv"
   },
   {
@@ -1163,9 +1151,6 @@
    "name" : "_ZNK7android7String86lengthEv"
   },
   {
-   "name" : "_ZNK7android7String88walkPathEPS0_"
-  },
-  {
    "name" : "_ZNK7android8String1610startsWithEPKDs"
   },
   {
@@ -6809,22 +6794,6 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
-   "function_name" : "android::String8::appendPath",
-   "linker_set_key" : "_ZN7android7String810appendPathEPKc",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    },
-    {
-     "referenced_type" : "_ZTIPKc"
-    }
-   ],
-   "return_type" : "_ZTIRN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String8::lockBuffer",
    "linker_set_key" : "_ZN7android7String810lockBufferEm",
    "parameters" :
@@ -9104,6 +9073,7 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "access" : "private",
    "function_name" : "android::String8::getPathDir",
    "linker_set_key" : "_ZNK7android7String810getPathDirEv",
    "parameters" :
@@ -9117,46 +9087,7 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
-   "function_name" : "android::String8::getBasePath",
-   "linker_set_key" : "_ZNK7android7String811getBasePathEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::getPathLeaf",
-   "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "access" : "private",
-   "function_name" : "android::String8::find_extension",
-   "linker_set_key" : "_ZNK7android7String814find_extensionEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIPc",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String8::getPathExtension",
    "linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
    "parameters" :
@@ -9203,23 +9134,6 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
-   "function_name" : "android::String8::walkPath",
-   "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    },
-    {
-     "default_arg" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String16::startsWith",
    "linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
    "parameters" :
diff --git a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
index 219c766..e8236ea 100644
--- a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
    "name" : "_ZN7android7RefBaseD2Ev"
   },
   {
-   "name" : "_ZN7android7String810appendPathEPKc"
-  },
-  {
    "name" : "_ZN7android7String810lockBufferEj"
   },
   {
@@ -1145,15 +1142,6 @@
    "name" : "_ZNK7android7String810getPathDirEv"
   },
   {
-   "name" : "_ZNK7android7String811getBasePathEv"
-  },
-  {
-   "name" : "_ZNK7android7String811getPathLeafEv"
-  },
-  {
-   "name" : "_ZNK7android7String814find_extensionEv"
-  },
-  {
    "name" : "_ZNK7android7String816getPathExtensionEv"
   },
   {
@@ -1163,9 +1151,6 @@
    "name" : "_ZNK7android7String86lengthEv"
   },
   {
-   "name" : "_ZNK7android7String88walkPathEPS0_"
-  },
-  {
    "name" : "_ZNK7android8String1610startsWithEPKDs"
   },
   {
@@ -6805,22 +6790,6 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
-   "function_name" : "android::String8::appendPath",
-   "linker_set_key" : "_ZN7android7String810appendPathEPKc",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    },
-    {
-     "referenced_type" : "_ZTIPKc"
-    }
-   ],
-   "return_type" : "_ZTIRN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String8::lockBuffer",
    "linker_set_key" : "_ZN7android7String810lockBufferEj",
    "parameters" :
@@ -9100,6 +9069,7 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "access" : "private",
    "function_name" : "android::String8::getPathDir",
    "linker_set_key" : "_ZNK7android7String810getPathDirEv",
    "parameters" :
@@ -9113,46 +9083,7 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
-   "function_name" : "android::String8::getBasePath",
-   "linker_set_key" : "_ZNK7android7String811getBasePathEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::getPathLeaf",
-   "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "access" : "private",
-   "function_name" : "android::String8::find_extension",
-   "linker_set_key" : "_ZNK7android7String814find_extensionEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIPc",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String8::getPathExtension",
    "linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
    "parameters" :
@@ -9199,23 +9130,6 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
-   "function_name" : "android::String8::walkPath",
-   "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    },
-    {
-     "default_arg" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String16::startsWith",
    "linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
    "parameters" :
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index b48b907..1fa3723 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -24,6 +24,11 @@
 #include <utils/String8.h>
 #include <utils/TypeHelpers.h>
 
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -56,9 +61,6 @@
     inline  const char16_t*     c_str() const;
     inline  const char16_t*     string() const;
 
-private:
-    static inline std::string   std_string(const String16& str);
-public:
             size_t              size() const;
     inline  bool                empty() const;
 
@@ -91,6 +93,7 @@
             bool                startsWith(const char16_t* prefix) const;
 
             bool                contains(const char16_t* chrs) const;
+    inline  bool                contains(const String16& other) const;
 
             status_t            replaceAll(char16_t replaceThis,
                                            char16_t withThis);
@@ -113,6 +116,12 @@
 
     inline                      operator const char16_t*() const;
 
+#ifdef HAS_STRING_VIEW
+    // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much
+    // lighter and if one needs, they can still create u16string from u16string_view.
+    inline                      operator std::u16string_view() const;
+#endif
+
     // Static and non-static String16 behave the same for the users, so
     // this method isn't of much use for the users. It is public for testing.
             bool                isStaticString() const;
@@ -249,11 +258,6 @@
     return mString;
 }
 
-inline std::string String16::std_string(const String16& str)
-{
-    return std::string(String8(str).c_str());
-}
-
 inline bool String16::empty() const
 {
     return length() == 0;
@@ -264,6 +268,11 @@
     return size();
 }
 
+inline bool String16::contains(const String16& other) const
+{
+    return contains(other.c_str());
+}
+
 inline String16& String16::operator=(const String16& other)
 {
     setTo(other);
@@ -353,8 +362,15 @@
     return mString;
 }
 
+inline String16::operator std::u16string_view() const
+{
+    return {mString, length()};
+}
+
 }  // namespace android
 
 // ---------------------------------------------------------------------------
 
+#undef HAS_STRING_VIEW
+
 #endif // ANDROID_STRING16_H
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index c798762..0b92f5b 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -18,7 +18,6 @@
 #define ANDROID_STRING8_H
 
 #include <iostream>
-#include <string>
 
 #include <utils/Errors.h>
 #include <utils/Unicode.h>
@@ -27,6 +26,16 @@
 #include <string.h> // for strcmp
 #include <stdarg.h>
 
+#if __has_include(<string>)
+#include <string>
+#define HAS_STRING
+#endif
+
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -58,10 +67,6 @@
     inline  const char*         c_str() const;
     inline  const char*         string() const;
 
-private:
-    static inline std::string   std_string(const String8& str);
-public:
-
     inline  size_t              size() const;
     inline  size_t              bytes() const;
     inline  bool                empty() const;
@@ -112,6 +117,10 @@
 
     inline                      operator const char*() const;
 
+#ifdef HAS_STRING_VIEW
+    inline explicit             operator std::string_view() const;
+#endif
+
             char*               lockBuffer(size_t size);
             void                unlockBuffer();
             status_t            unlockBuffer(size_t size);
@@ -119,90 +128,24 @@
             // return the index of the first byte of other in this at or after
             // start, or -1 if not found
             ssize_t             find(const char* other, size_t start = 0) const;
+    inline  ssize_t             find(const String8& other, size_t start = 0) const;
 
             // return true if this string contains the specified substring
     inline  bool                contains(const char* other) const;
+    inline  bool                contains(const String8& other) const;
 
             // removes all occurrence of the specified substring
             // returns true if any were found and removed
             bool                removeAll(const char* other);
+    inline  bool                removeAll(const String8& other);
 
             void                toLower();
 
-
-    /*
-     * These methods operate on the string as if it were a path name.
-     */
-
-    /*
-     * Get just the filename component.
-     *
-     * "/tmp/foo/bar.c" --> "bar.c"
-     */
-    String8 getPathLeaf(void) const;
-
-    /*
-     * Remove the last (file name) component, leaving just the directory
-     * name.
-     *
-     * "/tmp/foo/bar.c" --> "/tmp/foo"
-     * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
-     * "bar.c" --> ""
-     */
-    String8 getPathDir(void) const;
-
-    /*
-     * Retrieve the front (root dir) component.  Optionally also return the
-     * remaining components.
-     *
-     * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
-     * "/tmp" --> "tmp" (remain = "")
-     * "bar.c" --> "bar.c" (remain = "")
-     */
-    String8 walkPath(String8* outRemains = nullptr) const;
-
-    /*
-     * Return the filename extension.  This is the last '.' and any number
-     * of characters that follow it.  The '.' is included in case we
-     * decide to expand our definition of what constitutes an extension.
-     *
-     * "/tmp/foo/bar.c" --> ".c"
-     * "/tmp" --> ""
-     * "/tmp/foo.bar/baz" --> ""
-     * "foo.jpeg" --> ".jpeg"
-     * "foo." --> ""
-     */
-    String8 getPathExtension(void) const;
-
-    /*
-     * Return the path without the extension.  Rules for what constitutes
-     * an extension are described in the comment for getPathExtension().
-     *
-     * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
-     */
-    String8 getBasePath(void) const;
-
-    /*
-     * Add a component to the pathname.  We guarantee that there is
-     * exactly one path separator between the old path and the new.
-     * If there is no existing name, we just copy the new name in.
-     *
-     * If leaf is a fully qualified path (i.e. starts with '/', it
-     * replaces whatever was there before.
-     */
-    String8& appendPath(const char* leaf);
-    String8& appendPath(const String8& leaf) { return appendPath(leaf.c_str()); }
-
-    /*
-     * Like appendPath(), but does not affect this string.  Returns a new one instead.
-     */
-    String8 appendPathCopy(const char* leaf) const
-                                             { String8 p(*this); p.appendPath(leaf); return p; }
-    String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.c_str()); }
-
 private:
+            String8 getPathDir(void) const;
+            String8 getPathExtension(void) const;
+
             status_t            real_append(const char* other, size_t numChars);
-            char*               find_extension(void) const;
 
             const char* mString;
 
@@ -246,11 +189,6 @@
     return mString;
 }
 
-inline std::string String8::std_string(const String8& str)
-{
-    return std::string(str.c_str());
-}
-
 inline size_t String8::size() const
 {
     return length();
@@ -271,11 +209,26 @@
     return length();
 }
 
+inline ssize_t String8::find(const String8& other, size_t start) const
+{
+    return find(other.c_str(), start);
+}
+
 inline bool String8::contains(const char* other) const
 {
     return find(other) >= 0;
 }
 
+inline bool String8::contains(const String8& other) const
+{
+    return contains(other.c_str());
+}
+
+inline bool String8::removeAll(const String8& other)
+{
+    return removeAll(other.c_str());
+}
+
 inline String8& String8::operator=(const String8& other)
 {
     setTo(other);
@@ -384,8 +337,18 @@
     return mString;
 }
 
+#ifdef HAS_STRING_VIEW
+inline String8::operator std::string_view() const
+{
+    return {mString, length()};
+}
+#endif
+
 }  // namespace android
 
 // ---------------------------------------------------------------------------
 
+#undef HAS_STRING
+#undef HAS_STRING_VIEW
+
 #endif // ANDROID_STRING8_H
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 2f0ec8a..442bd15 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -13,7 +13,7 @@
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
-    onrestart restart media.tuner
+    onrestart restart --only-if-running media.tuner
     onrestart restart netd
     onrestart restart wificond
     task_profiles ProcessCapacityHigh
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 74a64c8..3422121 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -13,7 +13,7 @@
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
-    onrestart restart media.tuner
+    onrestart restart --only-if-running media.tuner
     onrestart restart netd
     onrestart restart wificond
     task_profiles ProcessCapacityHigh MaxPerformance
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e2c77c3..f65bb20 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -111,8 +111,7 @@
                 break;
             bits_size = res + 16;
             bits = realloc(bits, bits_size * 2);
-            if(bits == NULL)
-                err(1, "failed to allocate buffer of size %d\n", (int)bits_size);
+            if (bits == NULL) err(1, "failed to allocate buffer of size %zd", bits_size);
         }
         res2 = 0;
         switch(i) {