Merge "documenting optimized flash super" into main
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,6 +20,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 8241f0e..adf8738 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,6 +19,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 0cb8e08..972a575 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -26,6 +26,7 @@
 recvmsg: 1
 recvfrom: 1
 sysinfo: 1
+setsockopt: 1
 
 process_vm_readv: 1
 
diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
index 281e231..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.riscv64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
@@ -19,6 +19,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,6 +20,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 281e231..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,6 +19,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index e929f42..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));
@@ -661,6 +672,17 @@
         if (!android::gsi::DisableGsi()) {
             return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
         }
+    } else if (args[1] == "status") {
+        std::string active_dsu;
+        if (!android::gsi::IsGsiRunning()) {
+            device->WriteInfo("Not running");
+        } else if (!android::gsi::GetActiveDsu(&active_dsu)) {
+            return device->WriteFail(strerror(errno));
+        } else {
+            device->WriteInfo("Running active DSU: " + active_dsu);
+        }
+    } else {
+        return device->WriteFail("Invalid arguments");
     }
     return device->WriteStatus(FastbootResult::OKAY, "Success");
 }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 81787f5..21df729 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -350,23 +350,22 @@
 //
 // The returned Transport is a singleton, so multiple calls to this function will return the same
 // 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) {
+static std::unique_ptr<Transport> open_device(const char* local_serial,
+                                              bool wait_for_device = true,
+                                              bool announce = true) {
     const Result<NetworkSerial, FastbootError> network_serial = ParseNetworkSerial(local_serial);
 
-    Transport* transport = nullptr;
+    std::unique_ptr<Transport> transport;
     while (true) {
         if (network_serial.ok()) {
             std::string error;
             if (network_serial->protocol == Socket::Protocol::kTcp) {
-                transport = tcp::Connect(network_serial->address, network_serial->port, &error)
-                                    .release();
+                transport = tcp::Connect(network_serial->address, network_serial->port, &error);
             } else if (network_serial->protocol == Socket::Protocol::kUdp) {
-                transport = udp::Connect(network_serial->address, network_serial->port, &error)
-                                    .release();
+                transport = udp::Connect(network_serial->address, network_serial->port, &error);
             }
 
-            if (transport == nullptr && announce) {
+            if (!transport && announce) {
                 LOG(ERROR) << "error: " << error;
             }
         } else if (network_serial.error().code() ==
@@ -378,12 +377,12 @@
             Expect(network_serial);
         }
 
-        if (transport != nullptr) {
+        if (transport) {
             return transport;
         }
 
         if (!wait_for_device) {
-            return nullptr;
+            return transport;
         }
 
         if (announce) {
@@ -394,9 +393,9 @@
     }
 }
 
-static Transport* NetworkDeviceConnected(bool print = false) {
-    Transport* transport = nullptr;
-    Transport* result = nullptr;
+static std::unique_ptr<Transport> NetworkDeviceConnected(bool print = false) {
+    std::unique_ptr<Transport> transport;
+    std::unique_ptr<Transport> result;
 
     ConnectedDevicesStorage storage;
     std::set<std::string> devices;
@@ -409,11 +408,11 @@
         transport = open_device(device.c_str(), false, false);
 
         if (print) {
-            PrintDevice(device.c_str(), transport == nullptr ? "offline" : "fastboot");
+            PrintDevice(device.c_str(), transport ? "offline" : "fastboot");
         }
 
-        if (transport != nullptr) {
-            result = transport;
+        if (transport) {
+            result = std::move(transport);
         }
     }
 
@@ -431,21 +430,21 @@
 //
 // The returned Transport is a singleton, so multiple calls to this function will return the same
 // object, and the caller should not attempt to delete the returned Transport.
-static Transport* open_device() {
+static std::unique_ptr<Transport> open_device() {
     if (serial != nullptr) {
         return open_device(serial);
     }
 
     bool announce = true;
-    Transport* transport = nullptr;
+    std::unique_ptr<Transport> transport;
     while (true) {
         transport = usb_open(match_fastboot(nullptr));
-        if (transport != nullptr) {
+        if (transport) {
             return transport;
         }
 
         transport = NetworkDeviceConnected();
-        if (transport != nullptr) {
+        if (transport) {
             return transport;
         }
 
@@ -455,6 +454,8 @@
         }
         std::this_thread::sleep_for(std::chrono::seconds(1));
     }
+
+    return transport;
 }
 
 static int Connect(int argc, char* argv[]) {
@@ -466,8 +467,7 @@
     const char* local_serial = *argv;
     Expect(ParseNetworkSerial(local_serial));
 
-    const Transport* transport = open_device(local_serial, false);
-    if (transport == nullptr) {
+    if (!open_device(local_serial, false)) {
         return 1;
     }
 
@@ -531,6 +531,7 @@
     usb_open(list_devices_callback);
     NetworkDeviceConnected(/* print */ true);
 }
+
 void syntax_error(const char* fmt, ...) {
     fprintf(stderr, "fastboot: usage: ");
 
@@ -1547,9 +1548,7 @@
 
 void reboot_to_userspace_fastboot() {
     fb->RebootTo("fastboot");
-
-    auto* old_transport = fb->set_transport(nullptr);
-    delete old_transport;
+    fb->set_transport(nullptr);
 
     // Give the current connection time to close.
     std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -2372,8 +2371,8 @@
         return show_help();
     }
 
-    Transport* transport = open_device();
-    if (transport == nullptr) {
+    std::unique_ptr<Transport> transport = open_device();
+    if (!transport) {
         return 1;
     }
     fastboot::DriverCallbacks driver_callbacks = {
@@ -2383,7 +2382,7 @@
             .text = TextMessage,
     };
 
-    fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+    fastboot::FastBootDriver fastboot_driver(std::move(transport), driver_callbacks, false);
     fb = &fastboot_driver;
     fp->fb = &fastboot_driver;
 
@@ -2574,14 +2573,12 @@
                     std::make_unique<ResizeTask>(fp.get(), partition, size, fp->slot_override);
             resize_task->Run();
         } else if (command == "gsi") {
-            std::string arg = next_arg(&args);
-            if (arg == "wipe") {
-                fb->RawCommand("gsi:wipe", "wiping GSI");
-            } else if (arg == "disable") {
-                fb->RawCommand("gsi:disable", "disabling GSI");
-            } else {
-                syntax_error("expected 'wipe' or 'disable'");
+            if (args.empty()) syntax_error("invalid gsi command");
+            std::string cmd("gsi");
+            while (!args.empty()) {
+                cmd += ":" + next_arg(&args);
             }
+            fb->RawCommand(cmd, "");
         } else if (command == "wipe-super") {
             std::string image;
             if (args.empty()) {
@@ -2628,9 +2625,6 @@
     }
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
 
-    auto* old_transport = fb->set_transport(nullptr);
-    delete old_transport;
-
     return 0;
 }
 
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 9770ab2..e5ef66b 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -58,9 +58,10 @@
 namespace fastboot {
 
 /*************************** PUBLIC *******************************/
-FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
+FastBootDriver::FastBootDriver(std::unique_ptr<Transport> transport,
+                               DriverCallbacks driver_callbacks,
                                bool no_checks)
-    : transport_(transport),
+    : transport_(std::move(transport)),
       prolog_(std::move(driver_callbacks.prolog)),
       epilog_(std::move(driver_callbacks.epilog)),
       info_(std::move(driver_callbacks.info)),
@@ -627,9 +628,8 @@
     return 0;
 }
 
-Transport* FastBootDriver::set_transport(Transport* transport) {
-    std::swap(transport_, transport);
-    return transport;
+void FastBootDriver::set_transport(std::unique_ptr<Transport> transport) {
+    transport_ = std::move(transport);
 }
 
 }  // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 48b90ed..49cebd9 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -29,6 +29,7 @@
 #include <cstdlib>
 #include <functional>
 #include <limits>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -60,7 +61,7 @@
     static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
     static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
 
-    FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks = {},
+    FastBootDriver(std::unique_ptr<Transport> transport, DriverCallbacks driver_callbacks = {},
                    bool no_checks = false);
     ~FastBootDriver();
 
@@ -121,9 +122,7 @@
     std::string Error();
     RetCode WaitForDisconnect() override;
 
-    // Note: set_transport will return the previous transport.
-    Transport* set_transport(Transport* transport);
-    Transport* transport() const { return transport_; }
+    void set_transport(std::unique_ptr<Transport> transport);
 
     RetCode RawCommand(const std::string& cmd, const std::string& message,
                        std::string* response = nullptr, std::vector<std::string>* info = nullptr,
@@ -140,7 +139,7 @@
 
     std::string ErrnoStr(const std::string& msg);
 
-    Transport* transport_;
+    std::unique_ptr<Transport> transport_;
 
   private:
     RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);
diff --git a/fastboot/fastboot_driver_test.cpp b/fastboot/fastboot_driver_test.cpp
index 6f6cf8c..d2033b0 100644
--- a/fastboot/fastboot_driver_test.cpp
+++ b/fastboot/fastboot_driver_test.cpp
@@ -16,6 +16,7 @@
 
 #include "fastboot_driver.h"
 
+#include <memory>
 #include <optional>
 
 #include <gtest/gtest.h>
@@ -30,13 +31,14 @@
 };
 
 TEST_F(DriverTest, GetVar) {
-    MockTransport transport;
-    FastBootDriver driver(&transport);
+    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+    MockTransport* transport = transport_pointer.get();
+    FastBootDriver driver(std::move(transport_pointer));
 
-    EXPECT_CALL(transport, Write(_, _))
+    EXPECT_CALL(*transport, Write(_, _))
             .With(AllArgs(RawData("getvar:version")))
             .WillOnce(ReturnArg<1>());
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
 
     std::string output;
     ASSERT_EQ(driver.GetVar("version", &output), SUCCESS) << driver.Error();
@@ -44,14 +46,15 @@
 }
 
 TEST_F(DriverTest, InfoMessage) {
-    MockTransport transport;
-    FastBootDriver driver(&transport);
+    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+    MockTransport* transport = transport_pointer.get();
+    FastBootDriver driver(std::move(transport_pointer));
 
-    EXPECT_CALL(transport, Write(_, _))
+    EXPECT_CALL(*transport, Write(_, _))
             .With(AllArgs(RawData("oem dmesg")))
             .WillOnce(ReturnArg<1>());
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
 
     std::vector<std::string> info;
     ASSERT_EQ(driver.RawCommand("oem dmesg", "", nullptr, &info), SUCCESS) << driver.Error();
@@ -60,28 +63,29 @@
 }
 
 TEST_F(DriverTest, TextMessage) {
-    MockTransport transport;
     std::string text;
+    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+    MockTransport* transport = transport_pointer.get();
 
     DriverCallbacks callbacks{[](const std::string&) {}, [](int) {}, [](const std::string&) {},
                               [&text](const std::string& extra_text) { text += extra_text; }};
 
-    FastBootDriver driver(&transport, callbacks);
+    FastBootDriver driver(std::move(transport_pointer), callbacks);
 
-    EXPECT_CALL(transport, Write(_, _))
+    EXPECT_CALL(*transport, Write(_, _))
             .With(AllArgs(RawData("oem trusty runtest trusty.hwaes.bench")))
             .WillOnce(ReturnArg<1>());
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
-    EXPECT_CALL(transport, Read(_, _))
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
+    EXPECT_CALL(*transport, Read(_, _))
             .WillOnce(Invoke(
                     CopyData("TEXT, albeit very long and split over multiple TEXT messages.")));
-    EXPECT_CALL(transport, Read(_, _))
+    EXPECT_CALL(*transport, Read(_, _))
             .WillOnce(Invoke(CopyData("TEXT Indeed we can do that now with a TEXT message whenever "
                                       "we feel like it.")));
-    EXPECT_CALL(transport, Read(_, _))
+    EXPECT_CALL(*transport, Read(_, _))
             .WillOnce(Invoke(CopyData("TEXT Isn't that truly super cool?")));
 
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
 
     std::vector<std::string> info;
     ASSERT_EQ(driver.RawCommand("oem trusty runtest trusty.hwaes.bench", "", nullptr, &info),
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 9b5e5f7..94a53ed 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -128,7 +128,7 @@
             return MatchFastboot(info, device_serial);
         };
         for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
-            std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+            std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
             if (usb)
                 transport = std::unique_ptr<TransportSniffer>(
                         new TransportSniffer(std::move(usb), serial_port));
@@ -143,7 +143,7 @@
     } else {
         ASSERT_EQ(device_path, cb_scratch);  // The path can not change
     }
-    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
     // No error checking since non-A/B devices may not support the command
     fb->GetVar("current-slot", &initial_slot);
 }
@@ -200,7 +200,7 @@
     if (IsFastbootOverTcp()) {
         ConnectTcpFastbootDevice();
         device_path = cb_scratch;
-        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
         return;
     }
 
@@ -212,7 +212,7 @@
         return MatchFastboot(info, device_serial);
     };
     while (!transport) {
-        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+        std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
         if (usb) {
             transport = std::unique_ptr<TransportSniffer>(
                     new TransportSniffer(std::move(usb), serial_port));
@@ -220,7 +220,7 @@
         std::this_thread::sleep_for(1s);
     }
     device_path = cb_scratch;
-    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
 }
 
 void FastBootTest::SetLockState(bool unlock, bool assert_change) {
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 95778e1..79f3939 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -167,16 +167,15 @@
     const auto matcher = [](usb_ifc_info* info) -> int {
         return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
     };
-    Transport* transport = nullptr;
+    std::unique_ptr<Transport> transport;
     for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
         transport = usb_open(matcher);
         std::this_thread::sleep_for(std::chrono::milliseconds(10));
     }
-    ASSERT_NE(transport, nullptr) << "Could not find the fastboot device after: "
-                                  << 10 * FastBootTest::MAX_USB_TRIES << "ms";
+    ASSERT_NE(transport.get(), nullptr) << "Could not find the fastboot device after: "
+                                        << 10 * FastBootTest::MAX_USB_TRIES << "ms";
     if (transport) {
         transport->Close();
-        delete transport;
     }
 }
 
@@ -1892,7 +1891,7 @@
             return fastboot::FastBootTest::MatchFastboot(info,
                                                          fastboot::FastBootTest::device_serial);
         };
-        Transport* transport = nullptr;
+        std::unique_ptr<Transport> transport;
         while (!transport) {
             transport = usb_open(matcher);
             std::this_thread::sleep_for(std::chrono::milliseconds(10));
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 69581ab..d85cb81 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,6 +29,7 @@
 #pragma once
 
 #include <functional>
+#include <memory>
 
 #include "transport.h"
 
@@ -66,4 +67,4 @@
 typedef std::function<int(usb_ifc_info*)> ifc_match_func;
 
 // 0 is non blocking
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 964488c..37bb304 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -503,9 +503,15 @@
     return 0;
 }
 
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+    std::unique_ptr<UsbTransport> result;
     std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
-    return handle ? new LinuxUsbTransport(std::move(handle), timeout_ms) : nullptr;
+
+    if (handle) {
+        result = std::make_unique<LinuxUsbTransport>(std::move(handle), timeout_ms);
+    }
+
+    return result;
 }
 
 /* Wait for the system to notice the device is gone, so that a subsequent
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 8b852f5..28300b2 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -469,16 +469,20 @@
 /*
  * Definitions of this file's public functions.
  */
-
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+    std::unique_ptr<UsbTransport> result;
     std::unique_ptr<usb_handle> handle;
 
     if (init_usb(callback, &handle) < 0) {
         /* Something went wrong initializing USB. */
-        return nullptr;
+        return result;
     }
 
-    return new OsxUsbTransport(std::move(handle), timeout_ms);
+    if (handle) {
+        result = std::make_unique<OsxUsbTransport>(std::move(handle), timeout_ms);
+    }
+
+    return result;
 }
 
 OsxUsbTransport::~OsxUsbTransport() {
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 67bf8a3..56a6e7d 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -381,7 +381,13 @@
     return handle;
 }
 
-UsbTransport* usb_open(ifc_match_func callback, uint32_t) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t) {
+    std::unique_ptr<UsbTransport> result;
     std::unique_ptr<usb_handle> handle = find_usb_device(callback);
-    return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
+
+    if (handle) {
+        result = std::make_unique<WindowsUsbTransport>(std::move(handle));
+    }
+
+    return result;
 }
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 04b5a02..0131f73 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -446,6 +446,54 @@
     ],
 }
 
+cc_binary {
+    name: "create_snapshot",
+    host_supported: true,
+    device_supported: false,
+
+    srcs: ["libsnapshot_cow/create_cow.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    static_libs: [
+        "liblog",
+        "libbase",
+        "libext4_utils",
+        "libsnapshot_cow",
+        "libcrypto",
+        "libbrotli",
+        "libz",
+        "liblz4",
+        "libzstd",
+        "libgflags",
+    ],
+    shared_libs: [
+    ],
+
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+
+    dist: {
+        targets: [
+            "sdk",
+            "sdk-repo-platform-tools",
+            "sdk_repo",
+        ],
+    },
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+}
+
 python_library_host {
     name: "snapshot_proto_python",
     srcs: [
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index c056a19..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,11 +440,13 @@
     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;
     friend class LockTestConsumer;
     friend class SnapshotFuzzEnv;
+    friend class MapSnapshots;
     friend struct AutoDeleteCowImage;
     friend struct AutoDeleteSnapshot;
     friend struct PartitionCowCreator;
@@ -455,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_; }
@@ -548,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 = "");
@@ -662,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/create_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
new file mode 100644
index 0000000..4e07fe3
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
@@ -0,0 +1,359 @@
+#include <linux/types.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <condition_variable>
+#include <cstring>
+#include <future>
+#include <iostream>
+#include <limits>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <storage_literals/storage_literals.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+
+#include <openssl/sha.h>
+
+DEFINE_string(source, "", "Source partition image");
+DEFINE_string(target, "", "Target partition image");
+DEFINE_string(compression, "lz4",
+              "Compression algorithm. Default is set to lz4. Available options: lz4, zstd, gz");
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+using namespace android;
+using android::base::unique_fd;
+
+using android::snapshot::CreateCowWriter;
+using android::snapshot::ICowWriter;
+
+class CreateSnapshot {
+  public:
+    CreateSnapshot(const std::string& src_file, const std::string& target_file,
+                   const std::string& patch_file, const std::string& compression);
+    bool CreateSnapshotPatch();
+
+  private:
+    /* source.img */
+    std::string src_file_;
+    /* target.img */
+    std::string target_file_;
+    /* snapshot-patch generated */
+    std::string patch_file_;
+
+    /*
+     * Active file which is being parsed by this instance.
+     * It will either be source.img or target.img.
+     */
+    std::string parsing_file_;
+    bool create_snapshot_patch_ = false;
+
+    const int kNumThreads = 6;
+    const size_t kBlockSizeToRead = 1_MiB;
+
+    std::unordered_map<std::string, int> source_block_hash_;
+    std::mutex source_block_hash_lock_;
+
+    std::unique_ptr<ICowWriter> writer_;
+    std::mutex write_lock_;
+
+    std::unique_ptr<uint8_t[]> zblock_;
+
+    std::string compression_ = "lz4";
+    unique_fd fd_;
+
+    const int BLOCK_SZ = 4_KiB;
+    void SHA256(const void* data, size_t length, uint8_t out[32]);
+    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+    bool ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz);
+    std::string ToHexString(const uint8_t* buf, size_t len);
+
+    bool CreateSnapshotFile();
+    bool FindSourceBlockHash();
+    bool PrepareParse(std::string& parsing_file, const bool createSnapshot);
+    bool ParsePartition();
+    bool WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash);
+};
+
+void CreateSnapshotLogger(android::base::LogId, android::base::LogSeverity severity, const char*,
+                          const char*, unsigned int, const char* message) {
+    if (severity == android::base::ERROR) {
+        fprintf(stderr, "%s\n", message);
+    } else {
+        fprintf(stdout, "%s\n", message);
+    }
+}
+
+CreateSnapshot::CreateSnapshot(const std::string& src_file, const std::string& target_file,
+                               const std::string& patch_file, const std::string& compression)
+    : src_file_(src_file), target_file_(target_file), patch_file_(patch_file) {
+    if (!compression.empty()) {
+        compression_ = compression;
+    }
+}
+
+bool CreateSnapshot::PrepareParse(std::string& parsing_file, const bool createSnapshot) {
+    parsing_file_ = parsing_file;
+    create_snapshot_patch_ = createSnapshot;
+
+    if (createSnapshot) {
+        fd_.reset(open(patch_file_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "Failed to open the snapshot-patch file: " << patch_file_;
+            return false;
+        }
+
+        zblock_ = std::make_unique<uint8_t[]>(BLOCK_SZ);
+        std::memset(zblock_.get(), 0, BLOCK_SZ);
+
+        CowOptions options;
+        options.compression = compression_;
+        options.num_compress_threads = 2;
+        options.batch_write = true;
+        options.cluster_ops = 600;
+        writer_ = CreateCowWriter(2, options, std::move(fd_));
+    }
+    return true;
+}
+
+/*
+ * Create per-block sha256 hash of source partition
+ */
+bool CreateSnapshot::FindSourceBlockHash() {
+    if (!PrepareParse(src_file_, false)) {
+        return false;
+    }
+    return ParsePartition();
+}
+
+/*
+ * Create snapshot file by comparing sha256 per block
+ * of target.img with the constructed per-block sha256 hash
+ * of source partition.
+ */
+bool CreateSnapshot::CreateSnapshotFile() {
+    if (!PrepareParse(target_file_, true)) {
+        return false;
+    }
+    return ParsePartition();
+}
+
+/*
+ * Creates snapshot patch file by comparing source.img and target.img
+ */
+bool CreateSnapshot::CreateSnapshotPatch() {
+    if (!FindSourceBlockHash()) {
+        return false;
+    }
+    return CreateSnapshotFile();
+}
+
+void CreateSnapshot::SHA256(const void* data, size_t length, uint8_t out[32]) {
+    SHA256_CTX c;
+    SHA256_Init(&c);
+    SHA256_Update(&c, data, length);
+    SHA256_Final(out, &c);
+}
+
+std::string CreateSnapshot::ToHexString(const uint8_t* buf, size_t len) {
+    char lookup[] = "0123456789abcdef";
+    std::string out(len * 2 + 1, '\0');
+    char* outp = out.data();
+    for (; len > 0; len--, buf++) {
+        *outp++ = (char)lookup[*buf >> 4];
+        *outp++ = (char)lookup[*buf & 0xf];
+    }
+    return out;
+}
+
+bool CreateSnapshot::WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash) {
+    if (std::memcmp(zblock_.get(), buffer, BLOCK_SZ) == 0) {
+        std::lock_guard<std::mutex> lock(write_lock_);
+        return writer_->AddZeroBlocks(block, 1);
+    }
+
+    auto iter = source_block_hash_.find(block_hash);
+    if (iter != source_block_hash_.end()) {
+        std::lock_guard<std::mutex> lock(write_lock_);
+        return writer_->AddCopy(block, iter->second, 1);
+    }
+    std::lock_guard<std::mutex> lock(write_lock_);
+    return writer_->AddRawBlocks(block, buffer, BLOCK_SZ);
+}
+
+bool CreateSnapshot::ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "open failed: " << parsing_file_;
+        return false;
+    }
+
+    loff_t file_offset = offset;
+    const uint64_t read_sz = kBlockSizeToRead;
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+
+    while (true) {
+        size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+            LOG(ERROR) << "Failed to read block from block device: " << parsing_file_
+                       << " at offset: " << file_offset << " read-size: " << to_read
+                       << " block-size: " << dev_sz;
+            return false;
+        }
+
+        if (!IsBlockAligned(to_read)) {
+            LOG(ERROR) << "unable to parse the un-aligned request: " << to_read;
+            return false;
+        }
+
+        size_t num_blocks = to_read / BLOCK_SZ;
+        uint64_t buffer_offset = 0;
+        off_t foffset = file_offset;
+
+        while (num_blocks) {
+            const void* bufptr = (char*)buffer.get() + buffer_offset;
+            uint64_t blkindex = foffset / BLOCK_SZ;
+
+            uint8_t checksum[32];
+            SHA256(bufptr, BLOCK_SZ, checksum);
+            std::string hash = ToHexString(checksum, sizeof(checksum));
+
+            if (create_snapshot_patch_ && !WriteSnapshot(bufptr, blkindex, hash)) {
+                LOG(ERROR) << "WriteSnapshot failed for block: " << blkindex;
+                return false;
+            } else {
+                std::lock_guard<std::mutex> lock(source_block_hash_lock_);
+                {
+                    if (source_block_hash_.count(hash) == 0) {
+                        source_block_hash_[hash] = blkindex;
+                    }
+                }
+            }
+            buffer_offset += BLOCK_SZ;
+            foffset += BLOCK_SZ;
+            num_blocks -= 1;
+        }
+
+        file_offset += (skip_blocks * kBlockSizeToRead);
+        if (file_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    return true;
+}
+
+bool CreateSnapshot::ParsePartition() {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "open failed: " << parsing_file_;
+        return false;
+    }
+
+    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+    if (!dev_sz) {
+        LOG(ERROR) << "Could not determine block device size: " << parsing_file_;
+        return false;
+    }
+
+    if (!IsBlockAligned(dev_sz)) {
+        LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+        return false;
+    }
+
+    int num_threads = kNumThreads;
+
+    std::vector<std::future<bool>> threads;
+    off_t start_offset = 0;
+    const int skip_blocks = num_threads;
+
+    while (num_threads) {
+        threads.emplace_back(std::async(std::launch::async, &CreateSnapshot::ReadBlocks, this,
+                                        start_offset, skip_blocks, dev_sz));
+        start_offset += kBlockSizeToRead;
+        num_threads -= 1;
+        if (start_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    bool ret = true;
+    for (auto& t : threads) {
+        ret = t.get() && ret;
+    }
+
+    if (ret && create_snapshot_patch_ && !writer_->Finalize()) {
+        LOG(ERROR) << "Finzalize failed";
+        return false;
+    }
+
+    return ret;
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+constexpr char kUsage[] = R"(
+NAME
+    create_snapshot - Create snapshot patches by comparing two partition images
+
+SYNOPSIS
+    create_snapshot --source=<source.img> --target=<target.img> --compression="<compression-algorithm"
+
+    source.img -> Source partition image
+    target.img -> Target partition image
+    compressoin -> compression algorithm. Default set to lz4. Supported types are gz, lz4, zstd.
+
+EXAMPLES
+
+   $ create_snapshot $SOURCE_BUILD/system.img $TARGET_BUILD/system.img
+   $ create_snapshot $SOURCE_BUILD/product.img $TARGET_BUILD/product.img --compression="zstd"
+
+)";
+
+int main(int argc, char* argv[]) {
+    android::base::InitLogging(argv, &android::snapshot::CreateSnapshotLogger);
+    ::gflags::SetUsageMessage(kUsage);
+    ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+    if (FLAGS_source.empty() || FLAGS_target.empty()) {
+        LOG(INFO) << kUsage;
+        return 0;
+    }
+
+    std::string fname = android::base::Basename(FLAGS_target.c_str());
+    auto parts = android::base::Split(fname, ".");
+    std::string snapshotfile = parts[0] + ".patch";
+    android::snapshot::CreateSnapshot snapshot(FLAGS_source, FLAGS_target, snapshotfile,
+                                               FLAGS_compression);
+
+    if (!snapshot.CreateSnapshotPatch()) {
+        LOG(ERROR) << "Snapshot creation failed";
+        return -1;
+    }
+
+    LOG(INFO) << "Snapshot patch: " << snapshotfile << " created successfully";
+    return 0;
+}
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 38eb719..ebaca2d 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -17,14 +17,25 @@
 #include <sysexits.h>
 
 #include <chrono>
+#include <filesystem>
+#include <fstream>
+#include <future>
 #include <iostream>
 #include <map>
 #include <sstream>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
+#include <android-base/chrono_utils.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fstab/fstab.h>
@@ -33,6 +44,8 @@
 #include <libsnapshot/snapshot.h>
 #include <storage_literals/storage_literals.h>
 
+#include "partition_cow_creator.h"
+
 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
 #include <BootControlClient.h>
 #endif
@@ -56,13 +69,211 @@
                  "  merge\n"
                  "    Deprecated.\n"
                  "  map\n"
-                 "    Map all partitions at /dev/block/mapper\n";
+                 "    Map all partitions at /dev/block/mapper\n"
+                 "  map-snapshots <directory where snapshot patches are present>\n"
+                 "    Map all snapshots based on patches present in the directory\n"
+                 "  unmap-snapshots\n"
+                 "    Unmap all pre-created snapshots\n"
+                 "  delete-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;
 }
 
 namespace android {
 namespace snapshot {
 
+class MapSnapshots {
+  public:
+    MapSnapshots(std::string path = "");
+    bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
+    bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
+    bool FinishSnapshotWrites();
+    bool UnmapCowImagePath(std::string& name);
+    bool DeleteSnapshots();
+    bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
+    bool BeginUpdate();
+
+  private:
+    std::optional<std::string> GetCowImagePath(std::string& name);
+    bool WriteSnapshotPatch(std::string cow_device, std::string patch);
+    std::unique_ptr<SnapshotManager::LockedFile> lock_;
+    std::unique_ptr<SnapshotManager> sm_;
+    std::vector<std::future<bool>> threads_;
+    std::string snapshot_dir_path_;
+};
+
+MapSnapshots::MapSnapshots(std::string path) {
+    sm_ = SnapshotManager::New();
+    if (!sm_) {
+        std::cout << "Failed to create snapshotmanager";
+        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) {
+    std::string parsing_file = snapshot_dir_path_ + patchfile;
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "Failed to open file: " << parsing_file;
+        return false;
+    }
+
+    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+    if (!dev_sz) {
+        LOG(ERROR) << "Could not determine block device size: " << parsing_file;
+        return false;
+    }
+
+    const int block_sz = 4_KiB;
+    dev_sz += block_sz - 1;
+    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);
+
+    PartitionCowCreator cow_creator;
+    cow_creator.using_snapuserd = true;
+
+    if (!sm_->CreateSnapshot(lock_.get(), &cow_creator, &status)) {
+        LOG(ERROR) << "CreateSnapshot failed";
+        return false;
+    }
+
+    if (!sm_->CreateCowImage(lock_.get(), partition_name)) {
+        LOG(ERROR) << "CreateCowImage failed";
+        return false;
+    }
+
+    return true;
+}
+
+std::optional<std::string> MapSnapshots::GetCowImagePath(std::string& name) {
+    auto cow_dev = sm_->MapCowImage(name, 5s);
+    if (!cow_dev.has_value()) {
+        LOG(ERROR) << "Failed to get COW device path";
+        return std::nullopt;
+    }
+
+    LOG(INFO) << "COW Device path: " << cow_dev.value();
+    return cow_dev;
+}
+
+bool MapSnapshots::WriteSnapshotPatch(std::string cow_device, std::string patch) {
+    std::string patch_file = snapshot_dir_path_ + patch;
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(patch_file.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "Failed to open file: " << patch_file;
+        return false;
+    }
+
+    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+    if (!dev_sz) {
+        std::cout << "Could not determine block device size: " << patch_file;
+        return false;
+    }
+
+    android::base::unique_fd cfd(TEMP_FAILURE_RETRY(open(cow_device.c_str(), O_RDWR)));
+    if (cfd < 0) {
+        LOG(ERROR) << "Failed to open file: " << cow_device;
+        return false;
+    }
+
+    const uint64_t read_sz = 1_MiB;
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+    off_t file_offset = 0;
+
+    while (true) {
+        size_t to_read = std::min((dev_sz - file_offset), read_sz);
+        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+            PLOG(ERROR) << "ReadFullyAtOffset failed";
+            return false;
+        }
+
+        if (!android::base::WriteFullyAtOffset(cfd, buffer.get(), to_read, file_offset)) {
+            PLOG(ERROR) << "WriteFullyAtOffset failed";
+            return false;
+        }
+        file_offset += to_read;
+        if (file_offset >= dev_sz) {
+            break;
+        }
+    }
+    fsync(cfd.get());
+    return true;
+}
+
+bool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch) {
+    auto path = GetCowImagePath(pname);
+    if (!path.has_value()) {
+        return false;
+    }
+    threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch, this,
+                                     path.value(), snapshot_patch));
+    return true;
+}
+
+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";
+        if (!sm_->FinishedSnapshotWrites(false)) {
+            return false;
+        }
+        return sm_->BootFromSnapshotsWithoutSlotSwitch();
+    }
+
+    LOG(ERROR) << "Snapshot copy failed";
+    return false;
+}
+
+bool MapSnapshots::UnmapCowImagePath(std::string& name) {
+    return sm_->UnmapCowImage(name);
+}
+
+bool MapSnapshots::DeleteSnapshots() {
+    lock_ = sm_->LockExclusive();
+    if (!sm_->RemoveAllUpdateState(lock_.get())) {
+        LOG(ERROR) << "Remove All Update State failed";
+        return false;
+    }
+    return true;
+}
+
 bool DumpCmdHandler(int /*argc*/, char** argv) {
     android::base::InitLogging(argv, &android::base::StderrLogger);
     return SnapshotManager::New()->Dump(std::cout);
@@ -85,6 +296,134 @@
     return false;
 }
 
+bool GetVerityPartitions(std::vector<std::string>& partitions) {
+    auto& dm = android::dm::DeviceMapper::Instance();
+    auto dm_block_devices = dm.FindDmPartitions();
+    if (dm_block_devices.empty()) {
+        LOG(ERROR) << "No dm-enabled block device is found.";
+        return false;
+    }
+
+    for (auto& block_device : dm_block_devices) {
+        std::string dm_block_name = block_device.first;
+        std::string slot_suffix = fs_mgr_get_slot_suffix();
+        std::string partition = dm_block_name + slot_suffix;
+        partitions.push_back(partition);
+    }
+    return true;
+}
+
+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.";
+        return EXIT_FAILURE;
+    }
+
+    std::vector<std::string> partitions;
+    if (!GetVerityPartitions(partitions)) {
+        return false;
+    }
+
+    MapSnapshots snapshot;
+    for (auto partition : partitions) {
+        if (!snapshot.UnmapCowImagePath(partition)) {
+            LOG(ERROR) << "UnmapCowImagePath failed: " << partition;
+        }
+    }
+    return true;
+}
+
+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;
+    }
+
+    MapSnapshots snapshot;
+    return snapshot.DeleteSnapshots();
+}
+
+bool MapPrecreatedSnapshots(int argc, 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;
+    }
+
+    if (argc < 3) {
+        std::cerr << " map-snapshots <directory location where snapshot patches are present>"
+                     "    Map all snapshots based on patches present in the directory\n";
+        return false;
+    }
+
+    std::string path = std::string(argv[2]);
+    std::vector<std::string> patchfiles;
+
+    for (const auto& entry : std::filesystem::directory_iterator(path)) {
+        if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
+            patchfiles.push_back(android::base::Basename(entry.path().generic_string()));
+        }
+    }
+    auto& dm = android::dm::DeviceMapper::Instance();
+    auto dm_block_devices = dm.FindDmPartitions();
+    if (dm_block_devices.empty()) {
+        LOG(ERROR) << "No dm-enabled block device is found.";
+        return false;
+    }
+
+    std::vector<std::pair<std::string, std::string>> partitions;
+    for (auto& patchfile : patchfiles) {
+        auto npos = patchfile.rfind(".patch");
+        auto dm_block_name = patchfile.substr(0, npos);
+        if (dm_block_devices.find(dm_block_name) != dm_block_devices.end()) {
+            std::string slot_suffix = fs_mgr_get_slot_suffix();
+            std::string partition = dm_block_name + slot_suffix;
+            partitions.push_back(std::make_pair(partition, patchfile));
+        }
+    }
+
+    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;
+            return false;
+        }
+        if (!cow.InitiateThreadedSnapshotWrite(pair.first, pair.second)) {
+            LOG(ERROR) << "InitiateThreadedSnapshotWrite failed for: " << pair.first;
+            return false;
+        }
+    }
+
+    return cow.FinishSnapshotWrites();
+}
+
 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
 bool CreateTestUpdate(SnapshotManager* sm) {
     chromeos_update_engine::DeltaArchiveManifest manifest;
@@ -137,8 +476,8 @@
             .block_device = fs_mgr_get_super_partition_name(target_slot_number),
             .metadata_slot = {target_slot_number},
             .partition_name = system_target_name,
-            .partition_opener = &opener,
             .timeout_ms = 10s,
+            .partition_opener = &opener,
     };
     auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
     if (!writer) {
@@ -211,6 +550,10 @@
         {"test-blank-ota", TestOtaHandler},
 #endif
         {"unmap", UnmapCmdHandler},
+        {"map-snapshots", MapPrecreatedSnapshots},
+        {"unmap-snapshots", UnMapPrecreatedSnapshots},
+        {"delete-snapshots", DeletePrecreatedSnapshots},
+        {"revert-snapshots", RemovePrecreatedSnapshots},
         // clang-format on
 };
 
diff --git a/init/Android.bp b/init/Android.bp
index d4852d6..4c25ad7 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -174,7 +174,6 @@
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
-        "libsigningutils",
         "libsnapshot_cow",
         "libsnapshot_init",
         "libxml2",
@@ -183,7 +182,6 @@
     ],
     shared_libs: [
         "libbase",
-        "libcrypto",
         "libcutils",
         "libdl",
         "libext4_utils",
@@ -199,7 +197,6 @@
         "libselinux",
         "libunwindstack",
         "libutils",
-        "libziparchive",
     ],
     header_libs: ["bionic_libc_platform_headers"],
     bootstrap: true,
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 402b501..36ca379 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -8,14 +8,6 @@
     },
     {
       "name": "MicrodroidHostTestCases"
-    },
-    {
-      "name": "CtsSecurityHostTestCases",
-      "options": [
-        {
-          "include-filter": "android.security.cts.SeamendcHostTest"
-        }
-      ]
     }
   ],
   "hwasan-presubmit": [
@@ -27,14 +19,6 @@
     },
     {
       "name": "MicrodroidHostTestCases"
-    },
-    {
-      "name": "CtsSecurityHostTestCases",
-      "options": [
-        {
-          "include-filter": "android.security.cts.SeamendcHostTest"
-        }
-      ]
     }
   ]
 }
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 3239eb7..c6a287a 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -163,6 +163,21 @@
     return android::base::StringPrintf("_%zuk", page_size / 1024);
 }
 
+constexpr bool EndsWith(const std::string_view str, const std::string_view suffix) {
+    return str.size() >= suffix.size() &&
+           0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
+}
+
+constexpr std::string_view GetPageSizeSuffix(std::string_view dirname) {
+    if (EndsWith(dirname, "_16k")) {
+        return "_16k";
+    }
+    if (EndsWith(dirname, "_64k")) {
+        return "_64k";
+    }
+    return "";
+}
+
 }  // namespace
 
 std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {
@@ -211,7 +226,8 @@
     }
     dirent* entry = nullptr;
     std::vector<std::string> module_dirs;
-    const std::string release_specific_module_dir = uts.release + GetPageSizeSuffix();
+    const auto page_size_suffix = GetPageSizeSuffix();
+    const std::string release_specific_module_dir = uts.release + page_size_suffix;
     while ((entry = readdir(base_dir.get()))) {
         if (entry->d_type != DT_DIR) {
             continue;
@@ -223,6 +239,10 @@
             module_dirs.emplace_back(entry->d_name);
             break;
         }
+        // Ignore _16k/_64k module dirs on 4K kernels
+        if (GetPageSizeSuffix(entry->d_name) != page_size_suffix) {
+            continue;
+        }
         int dir_major = 0, dir_minor = 0;
         if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
             dir_minor != minor) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index f5de17d..2064fae 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1247,9 +1247,6 @@
         // Don't check for failure here, since we don't always have all of these partitions.
         // E.g. In case of recovery, the vendor partition will not have mounted and we
         // still need the system / platform properties to function.
-        if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
-            LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
-        }
         if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
             LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                      &property_infos);
@@ -1273,7 +1270,6 @@
         LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
-        LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index ac102eb..9095b85 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,29 +26,26 @@
 // The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
 // file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
 
-// The split policy is for supporting treble devices and updateable apexes.  It splits the SEPolicy
-// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
-// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
-// /odm/etc/selinux, and /dev/selinux (the apex portion of policy).  This is necessary to allow
-// images to be updated independently of the vendor image, while maintaining contributions from
-// multiple partitions in the SEPolicy.  This is especially important for VTS testing, where the
-// SEPolicy on the Google System Image may not be identical to the system image shipped on a
-// vendor's device.
+// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
+// portion of the policy).  This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
 
 // The split SEPolicy is loaded as described below:
 // 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
 //    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
-//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
-//    that were used to compile this precompiled policy.  The system partition contains a similar
-//    sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
-//    product, and apex contain sha256 hashes of their SEPolicy. Init loads this
+//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and
+//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
 //    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-//    /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
-//    respectively.
-// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
-//    them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
-//    to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by
-//    the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
+// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
+//    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
+//    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the
+//    OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
 //    That function contains even more documentation with the specific implementation details of how
 //    the SEPolicy is compiled if needed.
 
@@ -61,15 +58,12 @@
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <fstream>
 
-#include <CertUtils.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/result.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
@@ -77,7 +71,6 @@
 #include <libgsi/libgsi.h>
 #include <libsnapshot/snapshot.h>
 #include <selinux/android.h>
-#include <ziparchive/zip_archive.h>
 
 #include "block_dev_initializer.h"
 #include "debug_ramdisk.h"
@@ -245,7 +238,6 @@
              precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
             {"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
              precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
-            {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
     };
 
     for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -324,7 +316,7 @@
     // * vendor -- policy needed due to logic contained in the vendor image,
     // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
     //   with newer versions of platform policy.
-    // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
+    // * (optional) policy needed due to logic on product, system_ext, or odm images.
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
@@ -420,12 +412,6 @@
     if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
         odm_policy_cil_file.clear();
     }
-
-    // apex_sepolicy.cil is default but optional.
-    std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
-    if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
-        apex_policy_cil_file.clear();
-    }
     const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
     // clang-format off
@@ -468,9 +454,6 @@
     if (!odm_policy_cil_file.empty()) {
         compile_args.push_back(odm_policy_cil_file.c_str());
     }
-    if (!apex_policy_cil_file.empty()) {
-        compile_args.push_back(apex_policy_cil_file.c_str());
-    }
     compile_args.push_back(nullptr);
 
     if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -497,194 +480,6 @@
     return true;
 }
 
-constexpr const char* kSigningCertRelease =
-        "/system/etc/selinux/com.android.sepolicy.cert-release.der";
-const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
-const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
-const std::string kSepolicyZip = "SEPolicy.zip";
-const std::string kSepolicySignature = "SEPolicy.zip.sig";
-
-const std::string kTmpfsDir = "/dev/selinux/";
-
-// Files that are deleted after policy is compiled/loaded.
-const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
-// Files that need to persist because they are used by userspace processes.
-const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
-                                             "apex_service_contexts", "apex_seapp_contexts",
-                                             "apex_test"};
-
-Result<void> CreateTmpfsDir() {
-    mode_t mode = 0744;
-    struct stat stat_data;
-    if (stat(kTmpfsDir.c_str(), &stat_data) != 0) {
-        if (errno != ENOENT) {
-            return ErrnoError() << "Could not stat " << kTmpfsDir;
-        }
-        if (mkdir(kTmpfsDir.c_str(), mode) != 0) {
-            return ErrnoError() << "Could not mkdir " << kTmpfsDir;
-        }
-    } else {
-        if (!S_ISDIR(stat_data.st_mode)) {
-            return Error() << kTmpfsDir << " exists and is not a directory.";
-        }
-        LOG(WARNING) << "Directory " << kTmpfsDir << " already exists";
-    }
-
-    // Need to manually call chmod because mkdir will create a folder with
-    // permissions mode & ~umask.
-    if (chmod(kTmpfsDir.c_str(), mode) != 0) {
-        return ErrnoError() << "Could not chmod " << kTmpfsDir;
-    }
-
-    return {};
-}
-
-Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
-    ZipEntry entry;
-    std::string dstPath = kTmpfsDir + fileName;
-
-    int ret = FindEntry(archive, fileName, &entry);
-    if (ret != 0) {
-        // All files are optional. If a file doesn't exist, return without error.
-        return {};
-    }
-
-    unique_fd fd(TEMP_FAILURE_RETRY(
-            open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
-    if (fd == -1) {
-        return ErrnoError() << "Failed to open " << dstPath;
-    }
-
-    ret = ExtractEntryToFile(archive, &entry, fd.get());
-    if (ret != 0) {
-        return Error() << "Failed to extract entry \"" << fileName << "\" ("
-                       << entry.uncompressed_length << " bytes) to \"" << dstPath
-                       << "\": " << ErrorCodeString(ret);
-    }
-
-    return {};
-}
-
-Result<void> GetPolicyFromApex(const std::string& dir) {
-    LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
-    unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
-    if (fd < 0) {
-        return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
-    }
-
-    ZipArchiveHandle handle;
-    int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
-                            /*assume_ownership=*/false);
-    if (ret < 0) {
-        return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
-                       << ErrorCodeString(ret);
-    }
-
-    auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
-
-    auto create = CreateTmpfsDir();
-    if (!create.ok()) {
-        return create.error();
-    }
-
-    for (const auto& file : kApexSepolicy) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    for (const auto& file : kApexSepolicyTmp) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    return {};
-}
-
-Result<void> SepolicyCheckSignature(const std::string& dir) {
-    std::string signature;
-    if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
-        return ErrnoError() << "Failed to read " << kSepolicySignature;
-    }
-
-    std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
-    if (!sepolicyZip) {
-        return Error() << "Failed to open " << kSepolicyZip;
-    }
-    sepolicyZip.seekg(0);
-    std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
-                            std::istreambuf_iterator<char>());
-
-    auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
-    if (!releaseKey.ok()) {
-        return releaseKey.error();
-    }
-
-    return verifySignature(sepolicyStr, signature, *releaseKey);
-}
-
-Result<void> SepolicyVerify(const std::string& dir) {
-    auto sepolicySignature = SepolicyCheckSignature(dir);
-    if (!sepolicySignature.ok()) {
-        return Error() << "Apex SEPolicy failed signature check";
-    }
-    return {};
-}
-
-void CleanupApexSepolicy() {
-    for (const auto& file : kApexSepolicyTmp) {
-        std::string path = kTmpfsDir + file;
-        unlink(path.c_str());
-    }
-}
-
-// Updatable sepolicy is shipped within an zip within an APEX. Because
-// it needs to be available before Apexes are mounted, apexd copies
-// the zip from the APEX and stores it in /metadata/sepolicy. If there is
-// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
-// loaded from /system/etc/selinux/apex. Init performs the following
-// steps on boot:
-//
-// 1. Validates the zip by checking its signature against a public key that is
-// stored in /system/etc/selinux.
-// 2. Extracts files from zip and stores them in /dev/selinux.
-// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
-// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
-// is used. This is the same flow as on-device compilation of policy for Treble.
-// 4. Cleans up files in /dev/selinux which are no longer needed.
-// 5. Restorecons the remaining files in /dev/selinux.
-// 6. Sets selinux into enforcing mode and continues normal booting.
-//
-void PrepareApexSepolicy() {
-    // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
-    // /system. If neither exists, do nothing.
-    std::string dir;
-    if (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0) {
-        dir = kSepolicyApexMetadataDir;
-    } else if (access((kSepolicyApexSystemDir + kSepolicyZip).c_str(), F_OK) == 0) {
-        dir = kSepolicyApexSystemDir;
-    } else {
-        LOG(INFO) << "APEX Sepolicy not found";
-        return;
-    }
-
-    auto sepolicyVerify = SepolicyVerify(dir);
-    if (!sepolicyVerify.ok()) {
-        LOG(INFO) << "Error: " << sepolicyVerify.error();
-        // If signature verification fails, fall back to version on /system.
-        // This file doesn't need to be verified because it lives on the system partition which
-        // is signed and protected by verified boot.
-        dir = kSepolicyApexSystemDir;
-    }
-
-    auto apex = GetPolicyFromApex(dir);
-    if (!apex.ok()) {
-        // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
-        LOG(ERROR) << apex.error();
-    }
-}
-
 void ReadPolicy(std::string* policy) {
     PolicyFile policy_file;
 
@@ -961,12 +756,9 @@
 
     LOG(INFO) << "Opening SELinux policy";
 
-    PrepareApexSepolicy();
-
     // Read the policy before potentially killing snapuserd.
     std::string policy;
     ReadPolicy(&policy);
-    CleanupApexSepolicy();
 
     auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
     if (snapuserd_helper) {
@@ -982,13 +774,6 @@
         snapuserd_helper->FinishTransition();
         snapuserd_helper = nullptr;
     }
-
-    // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
-    // needed to transition files from tmpfs to *_contexts_file context should not be granted to
-    // any process after selinux is set into enforcing mode.
-    if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
-        PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
-    }
 }
 
 int SetupSelinux(char** argv) {
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 162f0f4..2c05fbc 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -185,10 +185,21 @@
         support_system_process: true,
     },
 
-    header_abi_checker: {
-        // AFDO affects weak symbols.
-        diff_flags: ["-allow-adding-removing-weak-symbols"],
-        ref_dump_dirs: ["abi-dumps"],
+    target: {
+        product: {
+            header_abi_checker: {
+                // AFDO affects weak symbols.
+                diff_flags: ["-allow-adding-removing-weak-symbols"],
+                ref_dump_dirs: ["abi-dumps"],
+            },
+        },
+        vendor: {
+            header_abi_checker: {
+                // AFDO affects weak symbols.
+                diff_flags: ["-allow-adding-removing-weak-symbols"],
+                ref_dump_dirs: ["abi-dumps"],
+            },
+        },
     },
 }
 
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index b48b907..ec2a324 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -56,9 +56,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;
 
@@ -249,11 +246,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;
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index ea25c6a..2a3d679 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -58,10 +58,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;
@@ -239,11 +235,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();
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) {