Merge "Fix potential miscellaneous debuggerd issues." into main
diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
index e4adf40..0def8ae 100644
--- a/debuggerd/crasher/arm/crashglue.S
+++ b/debuggerd/crasher/arm/crashglue.S
@@ -23,10 +23,11 @@
 	ldr lr, [lr]
 	b .
 	.cfi_endproc
+	.size crash1, .-crash1
 
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
 	.cfi_startproc
 	mov r1, sp
 	.cfi_def_cfa_register r1
@@ -35,3 +36,4 @@
 	ldr r0, [r0]
 	b .
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
index 97c824e..c56e19a 100644
--- a/debuggerd/crasher/arm64/crashglue.S
+++ b/debuggerd/crasher/arm64/crashglue.S
@@ -41,11 +41,12 @@
 	ldr x30, [x30]
 	b .
 	.cfi_endproc
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
 	.cfi_startproc
 	mov x1, sp
 	.cfi_def_cfa_register x1
@@ -54,3 +55,41 @@
 	ldr x0, [x0]
 	b .
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
+
+
+.globl crash_bti
+.type crash_bti, %function
+crash_bti:
+	.cfi_startproc
+	adr x16, 1f
+	br x16
+1:	// Deliberatly not a bti instruction so we crash here.
+	b .
+	.cfi_endproc
+	.size crash_bti, .-crash_bti
+
+
+.globl crash_pac
+.type crash_pac, %function
+crash_pac:
+	.cfi_startproc
+	paciasp
+	// Since sp is a pac input, this ensures a mismatch.
+	sub sp, sp, #16
+	autiasp
+	b .
+	.cfi_endproc
+	.size crash_pac, .-crash_pac
+
+// Set the PAC and BTI bits for this object file.
+.section .note.gnu.property, "a"
+.balign 8
+.long 4
+.long 0x10
+.long 0x5
+.asciz "GNU"
+.long 0xc0000000
+.long 4
+.long 0x3
+.long 0
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 12ba502..3b52776 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -19,6 +19,7 @@
 #include <assert.h>
 #include <dirent.h>
 #include <errno.h>
+#include <error.h>
 #include <fcntl.h>
 #include <pthread.h>
 #include <signal.h>
@@ -29,6 +30,9 @@
 #include <sys/prctl.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
 // We test both kinds of logging.
 #include <android-base/logging.h>
 #include <log/log.h>
@@ -59,8 +63,10 @@
 // Avoid name mangling so that stacks are more readable.
 extern "C" {
 
-void crash1(void);
-void crashnostack(void);
+void crash1();
+void crash_no_stack();
+void crash_bti();
+void crash_pac();
 
 int do_action(const char* arg);
 
@@ -196,13 +202,6 @@
     fprintf(stderr, "  fdsan_file            close a file descriptor that's owned by a FILE*\n");
     fprintf(stderr, "  fdsan_dir             close a file descriptor that's owned by a DIR*\n");
     fprintf(stderr, "  seccomp               fail a seccomp check\n");
-#if defined(__arm__)
-    fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
-    fprintf(stderr, "  kuser_get_tls         call kuser_get_tls\n");
-    fprintf(stderr, "  kuser_cmpxchg         call kuser_cmpxchg\n");
-    fprintf(stderr, "  kuser_memory_barrier  call kuser_memory_barrier\n");
-    fprintf(stderr, "  kuser_cmpxchg64       call kuser_cmpxchg64\n");
-#endif
     fprintf(stderr, "  xom                   read execute-only memory\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL      call liblog LOG_ALWAYS_FATAL\n");
@@ -223,6 +222,20 @@
     fprintf(stderr, "\n");
     fprintf(stderr, "  no_new_privs          set PR_SET_NO_NEW_PRIVS and then abort\n");
     fprintf(stderr, "\n");
+#if defined(__arm__)
+    fprintf(stderr, "Also, since this is an arm32 binary:\n");
+    fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
+    fprintf(stderr, "  kuser_get_tls         call kuser_get_tls\n");
+    fprintf(stderr, "  kuser_cmpxchg         call kuser_cmpxchg\n");
+    fprintf(stderr, "  kuser_memory_barrier  call kuser_memory_barrier\n");
+    fprintf(stderr, "  kuser_cmpxchg64       call kuser_cmpxchg64\n");
+#endif
+#if defined(__aarch64__)
+    fprintf(stderr, "Also, since this is an arm64 binary:\n");
+    fprintf(stderr, "  bti                   fail a branch target identification (BTI) check\n");
+    fprintf(stderr, "  pac                   fail a pointer authentication (PAC) check\n");
+#endif
+    fprintf(stderr, "\n");
     fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
     fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
     fprintf(stderr, "all available file descriptors before crashing.\n");
@@ -231,6 +244,21 @@
     return EXIT_FAILURE;
 }
 
+[[maybe_unused]] static void CheckCpuFeature(const std::string& name) {
+    std::string cpuinfo;
+    if (!android::base::ReadFileToString("/proc/cpuinfo", &cpuinfo)) {
+        error(1, errno, "couldn't read /proc/cpuinfo");
+    }
+    std::vector<std::string> lines = android::base::Split(cpuinfo, "\n");
+    for (std::string_view line : lines) {
+        if (!android::base::ConsumePrefix(&line, "Features\t:")) continue;
+        std::vector<std::string> features = android::base::Split(std::string(line), " ");
+        if (std::find(features.begin(), features.end(), name) == features.end()) {
+          error(1, 0, "/proc/cpuinfo does not report feature '%s'", name.c_str());
+        }
+    }
+}
+
 noinline int do_action(const char* arg) {
     // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
@@ -256,7 +284,7 @@
     } else if (!strcasecmp(arg, "stack-overflow")) {
       overflow_stack(nullptr);
     } else if (!strcasecmp(arg, "nostack")) {
-      crashnostack();
+      crash_no_stack();
     } else if (!strcasecmp(arg, "exit")) {
       exit(1);
     } else if (!strcasecmp(arg, "call-null")) {
@@ -350,6 +378,14 @@
     } else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
         return __kuser_cmpxchg64(0, 0, 0);
 #endif
+#if defined(__aarch64__)
+    } else if (!strcasecmp(arg, "bti")) {
+        CheckCpuFeature("bti");
+        crash_bti();
+    } else if (!strcasecmp(arg, "pac")) {
+        CheckCpuFeature("paca");
+        crash_pac();
+#endif
     } else if (!strcasecmp(arg, "no_new_privs")) {
         if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {
           fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\n", strerror(errno));
diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S
index 42f59b3..f179e33 100644
--- a/debuggerd/crasher/riscv64/crashglue.S
+++ b/debuggerd/crasher/riscv64/crashglue.S
@@ -43,10 +43,11 @@
 	ld t2, 0(zero)
 	j .
 	.cfi_endproc
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
 	.cfi_startproc
 	mv t1, sp
 	.cfi_def_cfa_register t1
@@ -54,3 +55,4 @@
 	ld t2, 0(zero)
 	j .
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
index e8eb3a7..453035b 100644
--- a/debuggerd/crasher/x86/crashglue.S
+++ b/debuggerd/crasher/x86/crashglue.S
@@ -6,13 +6,15 @@
 
 	movl $0, %edx
 	jmp *%edx
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
 	.cfi_startproc
 	movl %esp, %eax
 	.cfi_def_cfa_register %eax
 	movl $0, %esp
 	movl (%esp), %ebx
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
index 8f67214..c3d39c4 100644
--- a/debuggerd/crasher/x86_64/crashglue.S
+++ b/debuggerd/crasher/x86_64/crashglue.S
@@ -6,13 +6,15 @@
 
 	movl $0, %edx
 	jmp *%rdx
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
 	.cfi_startproc
 	movq %rsp, %rax
 	.cfi_def_cfa_register %rax
 	movq $0, %rsp
 	movq (%rsp), %rbx
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 1e5365d..01365f2 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -552,8 +552,14 @@
   }
 
   debugger_process_info process_info = {};
+  if (g_callbacks.get_process_info) {
+    process_info = g_callbacks.get_process_info();
+  }
   uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
   if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
+    // Applications can set abort messages via android_set_abort_message without
+    // actually aborting; ignore those messages in non-fatal dumps.
+    process_info.abort_msg = nullptr;
     if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
       // Allow for the abort message to be explicitly specified via the sigqueue value.
       // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
@@ -562,8 +568,6 @@
         info->si_ptr = reinterpret_cast<void*>(si_val & 1);
       }
     }
-  } else if (g_callbacks.get_process_info) {
-    process_info = g_callbacks.get_process_info();
   }
 
   gwp_asan_callbacks_t gwp_asan_callbacks = {};
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3644d95..71a228e 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1801,6 +1801,7 @@
     CancelSnapshotIfNeeded();
 
     tasks_ = CollectTasks();
+
     for (auto& task : tasks_) {
         task->Run();
     }
@@ -1815,7 +1816,18 @@
     } else {
         tasks = CollectTasksFromImageList();
     }
-
+    if (fp_->exclude_dynamic_partitions) {
+        auto is_non_static_flash_task = [](const auto& task) -> bool {
+            if (auto flash_task = task->AsFlashTask()) {
+                if (!should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
+                    return false;
+                }
+            }
+            return true;
+        };
+        tasks.erase(std::remove_if(tasks.begin(), tasks.end(), is_non_static_flash_task),
+                    tasks.end());
+    }
     return tasks;
 }
 
@@ -2219,6 +2231,7 @@
                                       {"disable-verification", no_argument, 0, 0},
                                       {"disable-verity", no_argument, 0, 0},
                                       {"disable-super-optimization", no_argument, 0, 0},
+                                      {"exclude-dynamic-partitions", no_argument, 0, 0},
                                       {"disable-fastboot-info", no_argument, 0, 0},
                                       {"force", no_argument, 0, 0},
                                       {"fs-options", required_argument, 0, 0},
@@ -2260,6 +2273,9 @@
                 g_disable_verity = true;
             } else if (name == "disable-super-optimization") {
                 fp->should_optimize_flash_super = false;
+            } else if (name == "exclude-dynamic-partitions") {
+                fp->exclude_dynamic_partitions = true;
+                fp->should_optimize_flash_super = false;
             } else if (name == "disable-fastboot-info") {
                 fp->should_use_fastboot_info = false;
             } else if (name == "force") {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index dc57149..75b8d29 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -99,6 +99,7 @@
     bool force_flash = false;
     bool should_optimize_flash_super = true;
     bool should_use_fastboot_info = true;
+    bool exclude_dynamic_partitions = false;
     uint64_t sparse_limit = 0;
 
     std::string slot_override;
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index bf64f0e..146064c 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -32,7 +32,7 @@
 
 void FlashTask::Run() {
     auto flash = [&](const std::string& partition) {
-        if (should_flash_in_userspace(partition) && !is_userspace_fastboot()) {
+        if (should_flash_in_userspace(partition) && !is_userspace_fastboot() && !fp_->force_flash) {
             die("The partition you are trying to flash is dynamic, and "
                 "should be flashed via fastbootd. Please run:\n"
                 "\n"
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 3a81f63..c9a4dee 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -161,6 +161,10 @@
     kCowCompressLz4 = 3,
     kCowCompressZstd = 4,
 };
+struct CowCompression {
+    CowCompressionAlgorithm algorithm = kCowCompressNone;
+    uint32_t compression_level = 0;
+};
 
 static constexpr uint8_t kCowReadAheadNotStarted = 0;
 static constexpr uint8_t kCowReadAheadInProgress = 1;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index d6194eb..74b8bb8 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -18,14 +18,11 @@
 
 #include <condition_variable>
 #include <cstdint>
-#include <future>
 #include <memory>
 #include <mutex>
 #include <optional>
 #include <queue>
 #include <string>
-#include <thread>
-#include <utility>
 #include <vector>
 
 #include <android-base/unique_fd.h>
@@ -110,16 +107,17 @@
 
 class CompressWorker {
   public:
-    CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size);
+    CompressWorker(CowCompression compression, uint32_t block_size);
     bool RunThread();
     void EnqueueCompressBlocks(const void* buffer, size_t num_blocks);
     bool GetCompressedBuffers(std::vector<std::basic_string<uint8_t>>* compressed_buf);
     void Finalize();
-    static std::basic_string<uint8_t> Compress(CowCompressionAlgorithm compression,
-                                               const void* data, size_t length);
+    static uint32_t GetDefaultCompressionLevel(CowCompressionAlgorithm compression);
+    static std::basic_string<uint8_t> Compress(CowCompression compression, const void* data,
+                                               size_t length);
 
-    static bool CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
-                               const void* buffer, size_t num_blocks,
+    static bool CompressBlocks(CowCompression compression, size_t block_size, const void* buffer,
+                               size_t num_blocks,
                                std::vector<std::basic_string<uint8_t>>* compressed_data);
 
   private:
@@ -130,7 +128,7 @@
         std::vector<std::basic_string<uint8_t>> compressed_data;
     };
 
-    CowCompressionAlgorithm compression_;
+    CowCompression compression_;
     uint32_t block_size_;
 
     std::queue<CompressWork> work_queue_;
@@ -139,7 +137,6 @@
     std::condition_variable cv_;
     bool stopped_ = false;
 
-    std::basic_string<uint8_t> Compress(const void* data, size_t length);
     bool CompressBlocks(const void* buffer, size_t num_blocks,
                         std::vector<std::basic_string<uint8_t>>* compressed_data);
 };
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index a4a0ad6..96d6016 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -46,24 +46,47 @@
     } else if (name == "none" || name.empty()) {
         return {kCowCompressNone};
     } else {
+        LOG(ERROR) << "unable to determine default compression algorithm for: " << name;
         return {};
     }
 }
 
-std::basic_string<uint8_t> CompressWorker::Compress(const void* data, size_t length) {
-    return Compress(compression_, data, length);
+// 1. Default compression level is determined by compression algorithm
+// 2. There might be compatibility issues if a value is changed here, as  some older versions of
+// Android will assume a different compression level, causing cow_size estimation differences that
+// will lead to OTA failure. Ensure that the device and OTA package use the same compression level
+// for OTA to succeed.
+uint32_t CompressWorker::GetDefaultCompressionLevel(CowCompressionAlgorithm compression) {
+    switch (compression) {
+        case kCowCompressGz: {
+            return Z_BEST_COMPRESSION;
+        }
+        case kCowCompressBrotli: {
+            return BROTLI_DEFAULT_QUALITY;
+        }
+        case kCowCompressLz4: {
+            break;
+        }
+        case kCowCompressZstd: {
+            return ZSTD_defaultCLevel();
+        }
+        case kCowCompressNone: {
+            break;
+        }
+    }
+    return 0;
 }
 
-std::basic_string<uint8_t> CompressWorker::Compress(CowCompressionAlgorithm compression,
-                                                    const void* data, size_t length) {
-    switch (compression) {
+std::basic_string<uint8_t> CompressWorker::Compress(CowCompression compression, const void* data,
+                                                    size_t length) {
+    switch (compression.algorithm) {
         case kCowCompressGz: {
             const auto bound = compressBound(length);
             std::basic_string<uint8_t> buffer(bound, '\0');
 
             uLongf dest_len = bound;
             auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
-                                length, Z_BEST_COMPRESSION);
+                                length, compression.compression_level);
             if (rv != Z_OK) {
                 LOG(ERROR) << "compress2 returned: " << rv;
                 return {};
@@ -81,8 +104,8 @@
 
             size_t encoded_size = bound;
             auto rv = BrotliEncoderCompress(
-                    BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
-                    reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
+                    compression.compression_level, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE,
+                    length, reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
             if (!rv) {
                 LOG(ERROR) << "BrotliEncoderCompress failed";
                 return {};
@@ -117,8 +140,8 @@
         }
         case kCowCompressZstd: {
             std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
-            const auto compressed_size =
-                    ZSTD_compress(buffer.data(), buffer.size(), data, length, 0);
+            const auto compressed_size = ZSTD_compress(buffer.data(), buffer.size(), data, length,
+                                                       compression.compression_level);
             if (compressed_size <= 0) {
                 LOG(ERROR) << "ZSTD compression failed " << compressed_size;
                 return {};
@@ -133,7 +156,7 @@
             return buffer;
         }
         default:
-            LOG(ERROR) << "unhandled compression type: " << compression;
+            LOG(ERROR) << "unhandled compression type: " << compression.algorithm;
             break;
     }
     return {};
@@ -143,7 +166,7 @@
     return CompressBlocks(compression_, block_size_, buffer, num_blocks, compressed_data);
 }
 
-bool CompressWorker::CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
+bool CompressWorker::CompressBlocks(CowCompression compression, size_t block_size,
                                     const void* buffer, size_t num_blocks,
                                     std::vector<std::basic_string<uint8_t>>* compressed_data) {
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer);
@@ -255,7 +278,7 @@
     cv_.notify_all();
 }
 
-CompressWorker::CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size)
+CompressWorker::CompressWorker(CowCompression compression, uint32_t block_size)
     : compression_(compression), block_size_(block_size) {}
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index da90cc0..3692c1a 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -18,6 +18,7 @@
 
 #include <array>
 #include <cstring>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -62,6 +63,8 @@
         return IDecompressor::Brotli();
     } else if (compressor == "gz") {
         return IDecompressor::Gz();
+    } else if (compressor == "zstd") {
+        return IDecompressor::Zstd();
     } else {
         return nullptr;
     }
@@ -211,10 +214,6 @@
     return true;
 }
 
-std::unique_ptr<IDecompressor> IDecompressor::Gz() {
-    return std::unique_ptr<IDecompressor>(new GzDecompressor());
-}
-
 class BrotliDecompressor final : public StreamDecompressor {
   public:
     ~BrotliDecompressor();
@@ -275,10 +274,6 @@
     return true;
 }
 
-std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
-    return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
-}
-
 class Lz4Decompressor final : public IDecompressor {
   public:
     ~Lz4Decompressor() override = default;
@@ -382,6 +377,14 @@
     }
 };
 
+std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
+    return std::make_unique<BrotliDecompressor>();
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Gz() {
+    return std::make_unique<GzDecompressor>();
+}
+
 std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
     return std::make_unique<Lz4Decompressor>();
 }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 31b9a58..2258d9f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -472,14 +472,15 @@
     if (strcmp(GetParam(), "none") == 0) {
         GTEST_SKIP();
     }
-
+    CowCompression compression;
     auto algorithm = CompressionAlgorithmFromString(GetParam());
     ASSERT_TRUE(algorithm.has_value());
+    compression.algorithm = algorithm.value();
 
     std::string expected = "The quick brown fox jumps over the lazy dog.";
     expected.resize(4096, '\0');
 
-    auto result = CompressWorker::Compress(*algorithm, expected.data(), expected.size());
+    auto result = CompressWorker::Compress(compression, expected.data(), expected.size());
     ASSERT_FALSE(result.empty());
 
     HorribleStream<uint8_t> stream(result);
@@ -1408,6 +1409,18 @@
     ASSERT_TRUE(iter->AtEnd());
 }
 
+TEST_F(CowTest, ParseOptionsTest) {
+    CowOptions options;
+    std::vector<std::pair<std::string, bool>> testcases = {
+            {"gz,4", true},   {"gz,4,4", false}, {"lz4,4", true}, {"brotli,4", true},
+            {"zstd,4", true}, {"zstd,x", false}, {"zs,4", false}, {"zstd.4", false}};
+    for (size_t i = 0; i < testcases.size(); i++) {
+        options.compression = testcases[i].first;
+        CowWriterV2 writer(options, GetCowFd());
+        ASSERT_EQ(writer.Initialize(), testcases[i].second);
+    }
+}
+
 TEST_F(CowTest, LegacyRevMergeOpItrTest) {
     CowOptions options;
     options.cluster_ops = 5;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index c549969..6d04c6a 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -20,8 +20,8 @@
 #include <sys/uio.h>
 #include <unistd.h>
 
+#include <future>
 #include <limits>
-#include <queue>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -39,6 +39,8 @@
 #include <sys/ioctl.h>
 #include <unistd.h>
 
+#include "android-base/parseint.h"
+#include "android-base/strings.h"
 #include "parser_v2.h"
 
 // The info messages here are spammy, but as useful for update_engine. Disable
@@ -119,12 +121,29 @@
 }
 
 bool CowWriterV2::ParseOptions() {
-    auto algorithm = CompressionAlgorithmFromString(options_.compression);
+    auto parts = android::base::Split(options_.compression, ",");
+
+    if (parts.size() > 2) {
+        LOG(ERROR) << "failed to parse compression parameters: invalid argument count: "
+                   << parts.size() << " " << options_.compression;
+        return false;
+    }
+    auto algorithm = CompressionAlgorithmFromString(parts[0]);
     if (!algorithm) {
         LOG(ERROR) << "unrecognized compression: " << options_.compression;
         return false;
     }
-    compression_ = *algorithm;
+    if (parts.size() > 1) {
+        if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+            LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
+            return false;
+        }
+    } else {
+        compression_.compression_level =
+                CompressWorker::GetDefaultCompressionLevel(algorithm.value());
+    }
+
+    compression_.algorithm = *algorithm;
 
     if (options_.cluster_ops == 1) {
         LOG(ERROR) << "Clusters must contain at least two operations to function.";
@@ -366,7 +385,7 @@
     while (num_blocks) {
         size_t pending_blocks = (std::min(kProcessingBlocks, num_blocks));
 
-        if (compression_ && num_compress_threads_ > 1) {
+        if (compression_.algorithm && num_compress_threads_ > 1) {
             if (!CompressBlocks(pending_blocks, iter)) {
                 return false;
             }
@@ -386,7 +405,7 @@
                 op.source = next_data_pos_;
             }
 
-            if (compression_) {
+            if (compression_.algorithm) {
                 auto data = [&, this]() {
                     if (num_compress_threads_ > 1) {
                         auto data = std::move(*buf_iter_);
@@ -398,7 +417,7 @@
                         return data;
                     }
                 }();
-                op.compression = compression_;
+                op.compression = compression_.algorithm;
                 op.data_length = static_cast<uint16_t>(data.size());
 
                 if (!WriteOperation(op, data.data(), data.size())) {
@@ -507,8 +526,8 @@
         }
     }
 
-    // Footer should be at the end of a file, so if there is data after the current block, end it
-    // and start a new cluster.
+    // Footer should be at the end of a file, so if there is data after the current block, end
+    // it and start a new cluster.
     if (cluster_size_ && current_data_size_ > 0) {
         EmitCluster();
         extra_cluster = true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
index 809ae57..3f357e0 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -14,6 +14,7 @@
 
 #pragma once
 
+#include <future>
 #include "writer_base.h"
 
 namespace android {
@@ -63,7 +64,7 @@
 
   private:
     CowFooter footer_{};
-    CowCompressionAlgorithm compression_ = kCowCompressNone;
+    CowCompression compression_;
     uint64_t current_op_pos_ = 0;
     uint64_t next_op_pos_ = 0;
     uint64_t next_data_pos_ = 0;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 86ff5f7..51389a0 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -729,6 +729,14 @@
         LOG(ERROR) << "Failed to remove status file " << file_path << ": " << error;
         return false;
     }
+
+    // This path may never exist. If it is present, then it's a stale
+    // snapshot status file. Just remove the file and log the message.
+    const std::string tmp_path = file_path + ".tmp";
+    if (!android::base::RemoveFileIfExists(tmp_path, &error)) {
+        LOG(ERROR) << "Failed to remove stale snapshot file " << tmp_path;
+    }
+
     return true;
 }
 
@@ -754,10 +762,10 @@
         return false;
     }
 
-    auto other_suffix = device_->GetOtherSlotSuffix();
+    auto current_slot_suffix = device_->GetSlotSuffix();
 
     for (const auto& snapshot : snapshots) {
-        if (android::base::EndsWith(snapshot, other_suffix)) {
+        if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
             // Allow the merge to continue, but log this unexpected case.
             LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
             continue;
@@ -1123,7 +1131,7 @@
         return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ListSnapshots);
     }
 
-    auto other_suffix = device_->GetOtherSlotSuffix();
+    auto current_slot_suffix = device_->GetSlotSuffix();
 
     bool cancelled = false;
     bool merging = false;
@@ -1131,9 +1139,9 @@
     bool wrong_phase = false;
     MergeFailureCode failure_code = MergeFailureCode::Ok;
     for (const auto& snapshot : snapshots) {
-        if (android::base::EndsWith(snapshot, other_suffix)) {
+        if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
             // This will have triggered an error message in InitiateMerge already.
-            LOG(INFO) << "Skipping merge validation of unexpected snapshot: " << snapshot;
+            LOG(ERROR) << "Skipping merge validation of unexpected snapshot: " << snapshot;
             continue;
         }
 
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 0a85489..3b6d26a 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -685,6 +685,17 @@
     }
     ASSERT_TRUE(sm->InitiateMerge());
 
+    // Create stale files in snapshot directory. Merge should skip these files
+    // as the suffix doesn't match the current slot.
+    auto tmp_path = test_device->GetMetadataDir() + "/snapshots/test_partition_b.tmp";
+    auto other_slot = test_device->GetMetadataDir() + "/snapshots/test_partition_a";
+
+    unique_fd fd(open(tmp_path.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+    ASSERT_GE(fd, 0);
+
+    fd.reset(open(other_slot.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+    ASSERT_GE(fd, 0);
+
     // The device should have been switched to a snapshot-merge target.
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
@@ -700,13 +711,23 @@
     ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
     ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
 
+    // Make sure that snapshot states are cleared and all stale files
+    // are deleted
+    {
+        ASSERT_TRUE(AcquireLock());
+        auto local_lock = std::move(lock_);
+        std::vector<std::string> snapshots;
+        ASSERT_TRUE(sm->ListSnapshots(local_lock.get(), &snapshots));
+        ASSERT_TRUE(snapshots.empty());
+    }
+
     // The device should no longer be a snapshot or snapshot-merge.
     ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
 
     // Test that we can read back the string we wrote to the snapshot. Note
     // that the base device is gone now. |snap_device| contains the correct
     // partition.
-    unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
+    fd.reset(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
     ASSERT_GE(fd, 0);
 
     std::string buffer(test_string.size(), '\0');
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index fe7f99c..6548cc8 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -257,5 +257,4 @@
     },
     auto_gen_config: true,
     require_root: false,
-    host_supported: true,
 }
diff --git a/init/init.cpp b/init/init.cpp
index da63fdc..4bb8eec 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -832,6 +832,12 @@
     CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
 
+    if (NeedsTwoMountNamespaces()) {
+        // /bootstrap-apex is used to mount "bootstrap" APEXes.
+        CHECKCALL(mount("tmpfs", "/bootstrap-apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                        "mode=0755,uid=0,gid=0"));
+    }
+
     // /linkerconfig is used to keep generated linker configuration
     CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 5b53d50..7918f23 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -66,15 +66,6 @@
     return ret;
 }
 
-// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
-// namespaces.
-static bool NeedsTwoMountNamespaces() {
-    if (IsRecoveryMode()) return false;
-    // In microdroid, there's only one set of APEXes in built-in directories include block devices.
-    if (IsMicrodroid()) return false;
-    return true;
-}
-
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -83,6 +74,15 @@
 
 }  // namespace
 
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+bool NeedsTwoMountNamespaces() {
+    if (IsRecoveryMode()) return false;
+    // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+    if (IsMicrodroid()) return false;
+    return true;
+}
+
 bool SetupMountNamespaces() {
     // Set the propagation type of / as shared so that any mounting event (e.g.
     // /data) is by default visible to all processes. When private mounting is
@@ -163,6 +163,23 @@
             PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
             return false;
         }
+
+        // Some components (e.g. servicemanager) need to access bootstrap
+        // APEXes from the default mount namespace. To achieve that, we bind-mount
+        // /apex to /bootstrap-apex in the bootstrap mount namespace. Since /bootstrap-apex
+        // is "shared", the mounts are visible in the default mount namespace as well.
+        //
+        // The end result will look like:
+        //   in the bootstrap mount namespace:
+        //     /apex  (== /bootstrap-apex)
+        //       {bootstrap APEXes from the read-only partition}
+        //
+        //   in the default mount namespace:
+        //     /bootstrap-apex
+        //       {bootstrap APEXes from the read-only partition}
+        //     /apex
+        //       {APEXes, can be from /data partition}
+        if (!(BindMount("/bootstrap-apex", "/apex"))) return false;
     } else {
         // Otherwise, default == bootstrap
         default_ns_fd.reset(OpenMountNamespace());
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index 5e3dab2..43c5476 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -24,9 +24,12 @@
 enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
 
 bool SetupMountNamespaces();
+
 base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
 
 base::Result<MountNamespace> GetCurrentMountNamespace();
 
+bool NeedsTwoMountNamespaces();
+
 }  // namespace init
 }  // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index f34474f..ebdcaa6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -757,7 +757,7 @@
     selinux_android_restorecon("/dev/device-mapper", 0);
 
     selinux_android_restorecon("/apex", 0);
-
+    selinux_android_restorecon("/bootstrap-apex", 0);
     selinux_android_restorecon("/linkerconfig", 0);
 
     // adb remount, snapshot-based updates, and DSUs all create files during
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 92486e3..55a8694 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -162,7 +162,6 @@
         "properties.cpp",
         "record_stream.cpp",
         "strlcpy.c",
-        "threads.cpp",
     ],
 
     target: {
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 92564b8..9bc3429 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -13,13 +13,3 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#pragma once
-
-#include  <sys/types.h>
-
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
deleted file mode 100644
index cca50c1..0000000
--- a/libcutils/threads.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-** Copyright (C) 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <sys/types.h>
-
-#if defined(__APPLE__)
-#include <pthread.h>
-#include <stdint.h>
-#elif defined(__linux__)
-#include <pthread.h>
-#include <syscall.h>
-#include <unistd.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 30
-// No definition needed for Android because we'll just pick up bionic's copy.
-// No definition needed for Glibc >= 2.30 because it exposes its own copy.
-#else
-extern "C" pid_t gettid() {
-#if defined(__APPLE__)
-  uint64_t tid;
-  pthread_threadid_np(NULL, &tid);
-  return tid;
-#elif defined(__linux__)
-  return syscall(__NR_gettid);
-#elif defined(_WIN32)
-  return GetCurrentThreadId();
-#endif
-}
-#endif
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3362872..5218753 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -91,7 +91,7 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
+    dev proc sys system data data_mirror odm oem acct config storage mnt apex bootstrap-apex debug_ramdisk \
     linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \