Merge changes from topic "revert-2819069-LYZVLDOFQA" into main

* changes:
  Revert "libprocessgroup: Poll on cgroup.events"
  Revert "libprocessgroup: Use cgroup.kill"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 267571b..31e284d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -186,6 +186,41 @@
     export_include_dirs: ["include"],
 }
 
+cc_library {
+    name: "libdebuggerd_tombstone_proto_to_text",
+    defaults: ["debuggerd_defaults"],
+    ramdisk_available: true,
+    recovery_available: true,
+    vendor_ramdisk_available: true,
+
+    local_include_dirs: ["libdebuggerd/include"],
+    export_include_dirs: ["libdebuggerd/include"],
+
+    srcs: [
+        "libdebuggerd/tombstone_proto_to_text.cpp",
+    ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    static_libs: [
+        "libbase",
+        "liblog_for_runtime_apex",
+        "libunwindstack",
+    ],
+
+    whole_static_libs: [
+        "libtombstone_proto",
+        "libprotobuf-cpp-lite",
+    ],
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.runtime",
+    ],
+}
+
 cc_library_static {
     name: "libdebuggerd",
     defaults: ["debuggerd_defaults"],
@@ -199,7 +234,6 @@
         "libdebuggerd/open_files_list.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/tombstone_proto.cpp",
-        "libdebuggerd/tombstone_proto_to_text.cpp",
         "libdebuggerd/utility.cpp",
     ],
 
@@ -225,6 +259,7 @@
     ],
 
     whole_static_libs: [
+        "libdebuggerd_tombstone_proto_to_text",
         "libasync_safe",
         "gwp_asan_crash_handler",
         "libtombstone_proto",
@@ -309,7 +344,7 @@
         "libdebuggerd/test/elf_fake.cpp",
         "libdebuggerd/test/log_fake.cpp",
         "libdebuggerd/test/open_files_list_test.cpp",
-        "libdebuggerd/test/utility_test.cpp",
+        "libdebuggerd/test/tombstone_proto_to_text_test.cpp",
     ],
 
     target: {
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 198de37..26c2cd4 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -91,8 +91,6 @@
 void get_signal_sender(char* buf, size_t n, const siginfo_t*);
 const char* get_signame(const siginfo_t*);
 const char* get_sigcode(const siginfo_t*);
-std::string describe_tagged_addr_ctrl(long ctrl);
-std::string describe_pac_enabled_keys(long keys);
 
 // Number of bytes per MTE granule.
 constexpr size_t kTagGranuleSize = 16;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 837f406..ea8dff4 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -78,7 +78,8 @@
   }
 
   __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),
-                         region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
+                         __scudo_get_stack_depot_size(), region_info.get(), ring_buffer.get(),
+                         process_info.scudo_ring_buffer_size, memory.get(), memory_tags.get(),
                          memory_begin, memory_end - memory_begin);
 }
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
new file mode 100644
index 0000000..ac92ac0
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <sys/prctl.h>
+
+#include <string>
+
+#include <android-base/test_utils.h>
+
+#include "libdebuggerd/tombstone.h"
+#include "tombstone.pb.h"
+
+using CallbackType = std::function<void(const std::string& line, bool should_log)>;
+
+class TombstoneProtoToTextTest : public ::testing::Test {
+ public:
+  void SetUp() {
+    tombstone_.reset(new Tombstone);
+
+    tombstone_->set_arch(Architecture::ARM64);
+    tombstone_->set_build_fingerprint("Test fingerprint");
+    tombstone_->set_timestamp("1970-01-01 00:00:00");
+    tombstone_->set_pid(100);
+    tombstone_->set_tid(100);
+    tombstone_->set_uid(0);
+    tombstone_->set_selinux_label("none");
+
+    Signal signal;
+    signal.set_number(SIGSEGV);
+    signal.set_name("SIGSEGV");
+    signal.set_code(0);
+    signal.set_code_name("none");
+
+    *tombstone_->mutable_signal_info() = signal;
+
+    Thread thread;
+    thread.set_id(100);
+    thread.set_name("main");
+    thread.set_tagged_addr_ctrl(0);
+    thread.set_pac_enabled_keys(0);
+
+    auto& threads = *tombstone_->mutable_threads();
+    threads[100] = thread;
+    main_thread_ = &threads[100];
+  }
+
+  void ProtoToString() {
+    text_ = "";
+    EXPECT_TRUE(
+        tombstone_proto_to_text(*tombstone_, [this](const std::string& line, bool should_log) {
+          if (should_log) {
+            text_ += "LOG ";
+          }
+          text_ += line + '\n';
+        }));
+  }
+
+  Thread* main_thread_;
+  std::string text_;
+  std::unique_ptr<Tombstone> tombstone_;
+};
+
+TEST_F(TombstoneProtoToTextTest, tagged_addr_ctrl) {
+  main_thread_->set_tagged_addr_ctrl(0);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000000\\n");
+
+  main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000001 \\(PR_TAGGED_ADDR_ENABLE\\)\\n");
+
+  main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+                                     (0xfffe << PR_MTE_TAG_SHIFT));
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG tagged_addr_ctrl: 000000000007fff3 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, "
+               "mask 0xfffe\\)\\n");
+
+  main_thread_->set_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+                                     PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT));
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG tagged_addr_ctrl: 00000000f007fff7 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, "
+               "PR_MTE_TCF_ASYNC, mask 0xfffe, unknown 0xf0000000\\)\\n");
+}
+
+TEST_F(TombstoneProtoToTextTest, pac_enabled_keys) {
+  main_thread_->set_pac_enabled_keys(0);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000000\\n");
+
+  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000001 \\(PR_PAC_APIAKEY\\)\\n");
+
+  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY);
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG pac_enabled_keys: 0000000000000009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY\\)\\n");
+
+  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000);
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG pac_enabled_keys: 0000000000001009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown "
+               "0x1000\\)\\n");
+}
diff --git a/debuggerd/libdebuggerd/test/utility_test.cpp b/debuggerd/libdebuggerd/test/utility_test.cpp
deleted file mode 100644
index dad3380..0000000
--- a/debuggerd/libdebuggerd/test/utility_test.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <sys/prctl.h>
-
-#include "libdebuggerd/utility.h"
-
-TEST(UtilityTest, describe_tagged_addr_ctrl) {
-  EXPECT_EQ("", describe_tagged_addr_ctrl(0));
-  EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE)", describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE));
-  EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe)",
-            describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
-                                      (0xfffe << PR_MTE_TAG_SHIFT)));
-  EXPECT_EQ(
-      " (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, PR_MTE_TCF_ASYNC, mask 0xfffe, unknown "
-      "0xf0000000)",
-      describe_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
-                                PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT)));
-}
-
-TEST(UtilityTest, describe_pac_enabled_keys) {
-  EXPECT_EQ("", describe_pac_enabled_keys(0));
-  EXPECT_EQ(" (PR_PAC_APIAKEY)", describe_pac_enabled_keys(PR_PAC_APIAKEY));
-  EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY)",
-            describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY));
-  EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown 0x1000)",
-            describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000));
-}
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index e44dc10..ad91320 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -29,6 +29,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <bionic/macros.h>
+#include <sys/prctl.h>
 
 #include "tombstone.pb.h"
 
@@ -40,6 +41,42 @@
 #define CBS(...) CB(false, __VA_ARGS__)
 using CallbackType = std::function<void(const std::string& line, bool should_log)>;
 
+#define DESCRIBE_FLAG(flag) \
+  if (value & flag) {       \
+    desc += ", ";           \
+    desc += #flag;          \
+    value &= ~flag;         \
+  }
+
+static std::string describe_end(long value, std::string& desc) {
+  if (value) {
+    desc += StringPrintf(", unknown 0x%lx", value);
+  }
+  return desc.empty() ? "" : " (" + desc.substr(2) + ")";
+}
+
+static std::string describe_tagged_addr_ctrl(long value) {
+  std::string desc;
+  DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
+  DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
+  DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
+  if (value & PR_MTE_TAG_MASK) {
+    desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
+    value &= ~PR_MTE_TAG_MASK;
+  }
+  return describe_end(value, desc);
+}
+
+static std::string describe_pac_enabled_keys(long value) {
+  std::string desc;
+  DESCRIBE_FLAG(PR_PAC_APIAKEY);
+  DESCRIBE_FLAG(PR_PAC_APIBKEY);
+  DESCRIBE_FLAG(PR_PAC_APDAKEY);
+  DESCRIBE_FLAG(PR_PAC_APDBKEY);
+  DESCRIBE_FLAG(PR_PAC_APGAKEY);
+  return describe_end(value, desc);
+}
+
 static const char* abi_string(const Tombstone& tombstone) {
   switch (tombstone.arch()) {
     case Architecture::ARM32:
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 15f09b3..742ac7c 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -445,42 +445,6 @@
   return "?";
 }
 
-#define DESCRIBE_FLAG(flag) \
-  if (value & flag) {       \
-    desc += ", ";           \
-    desc += #flag;          \
-    value &= ~flag;         \
-  }
-
-static std::string describe_end(long value, std::string& desc) {
-  if (value) {
-    desc += StringPrintf(", unknown 0x%lx", value);
-  }
-  return desc.empty() ? "" : " (" + desc.substr(2) + ")";
-}
-
-std::string describe_tagged_addr_ctrl(long value) {
-  std::string desc;
-  DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
-  DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
-  DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
-  if (value & PR_MTE_TAG_MASK) {
-    desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
-    value &= ~PR_MTE_TAG_MASK;
-  }
-  return describe_end(value, desc);
-}
-
-std::string describe_pac_enabled_keys(long value) {
-  std::string desc;
-  DESCRIBE_FLAG(PR_PAC_APIAKEY);
-  DESCRIBE_FLAG(PR_PAC_APIBKEY);
-  DESCRIBE_FLAG(PR_PAC_APDAKEY);
-  DESCRIBE_FLAG(PR_PAC_APDBKEY);
-  DESCRIBE_FLAG(PR_PAC_APGAKEY);
-  return describe_end(value, desc);
-}
-
 void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
                    unwindstack::AndroidUnwinderData& data, const char* prefix) {
   std::set<std::string> unreadable_elf_files;
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 804f805..7be5d61 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -39,3 +39,17 @@
     recovery_available: true,
     vendor_ramdisk_available: true,
 }
+
+java_library_static {
+    name: "libtombstone_proto_java",
+    proto: {
+        type: "lite",
+    },
+    srcs: [
+        "tombstone.proto",
+    ],
+    jarjar_rules: "jarjar-rules.txt",
+    sdk_version: "current",
+    static_libs: ["libprotobuf-java-lite"],
+}
+
diff --git a/debuggerd/proto/jarjar-rules.txt b/debuggerd/proto/jarjar-rules.txt
new file mode 100644
index 0000000..66878a9
--- /dev/null
+++ b/debuggerd/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.server.os.protobuf.@1
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 25c5a6e..0947ff9 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -224,7 +224,7 @@
     auto remove_if_callback = [&](const auto& task) -> bool {
         if (auto flash_task = task->AsFlashTask()) {
             return helper->WillFlash(flash_task->GetPartitionAndSlot());
-        } else if (auto update_super_task = task->AsUpdateSuperTask()) {
+        } else if (task->AsUpdateSuperTask()) {
             return true;
         } else if (auto reboot_task = task->AsRebootTask()) {
             if (reboot_task->GetTarget() == "fastboot") {
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 9cde1a8..81154c6 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -354,7 +354,7 @@
                                 const std::vector<std::unique_ptr<Task>>& tasks) {
             bool contains_optimized_task = false;
             for (auto& task : tasks) {
-                if (auto optimized_task = task->AsOptimizedFlashSuperTask()) {
+                if (task->AsOptimizedFlashSuperTask()) {
                     contains_optimized_task = true;
                 }
                 if (auto flash_task = task->AsFlashTask()) {
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 324f50a..edecd7c 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -25,7 +25,7 @@
     {
       "name": "vab_legacy_tests"
     },
-    // TODO: b/279009697
+    // TODO(b/279009697):
     //{"name": "vabc_legacy_tests"},
     {
       "name": "cow_api_test"
@@ -47,7 +47,7 @@
     {
       "name": "vab_legacy_tests"
     },
-    // TODO: b/279009697
+    // TODO(b/279009697):
     //{"name": "vabc_legacy_tests"}
     {
       "name": "snapuserd_test"
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 35c8c63..a94a274 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1244,17 +1244,27 @@
 };
 
 std::string fs_mgr_find_bow_device(const std::string& block_device) {
-    if (block_device.substr(0, 5) != "/dev/") {
-        LOG(ERROR) << "Expected block device, got " << block_device;
-        return std::string();
+    // handle symlink such as "/dev/block/mapper/userdata"
+    std::string real_path;
+    if (!android::base::Realpath(block_device, &real_path)) {
+        real_path = block_device;
     }
 
-    std::string sys_dir = std::string("/sys/") + block_device.substr(5);
-
+    struct stat st;
+    if (stat(real_path.c_str(), &st) < 0) {
+        PLOG(ERROR) << "stat failed: " << real_path;
+        return std::string();
+    }
+    if (!S_ISBLK(st.st_mode)) {
+        PLOG(ERROR) << real_path << " is not block device";
+        return std::string();
+    }
+    std::string sys_dir = android::base::StringPrintf("/sys/dev/block/%u:%u", major(st.st_rdev),
+                                                      minor(st.st_rdev));
     for (;;) {
         std::string name;
         if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) {
-            PLOG(ERROR) << block_device << " is not dm device";
+            PLOG(ERROR) << real_path << " is not dm device";
             return std::string();
         }
 
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 622f181..8e76150 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -136,6 +136,7 @@
     /* Format the partition using the calculated length */
 
     const auto size_str = std::to_string(dev_sz / getpagesize());
+    std::string block_size = std::to_string(getpagesize());
 
     std::vector<const char*> args = {"/system/bin/make_f2fs", "-g", "android"};
     if (needs_projid) {
@@ -154,6 +155,10 @@
         args.push_back("-O");
         args.push_back("extra_attr");
     }
+    args.push_back("-w");
+    args.push_back(block_size.c_str());
+    args.push_back("-b");
+    args.push_back(block_size.c_str());
     if (!zoned_device.empty()) {
         args.push_back("-c");
         args.push_back(zoned_device.c_str());
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 9be236d..06214ef 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -356,6 +356,8 @@
         fs_type = "f2fs";
         command = kMkF2fs + " -w "s;
         command += std::to_string(getpagesize());
+        command = kMkF2fs + " -b "s;
+        command += std::to_string(getpagesize());
         command += " -f -d1 -l" + android::base::Basename(kScratchMountPoint);
     } else if (!access(kMkExt4, X_OK) && fs_mgr_filesystem_available("ext4")) {
         fs_type = "ext4";
@@ -854,21 +856,6 @@
         return;
     }
 
-    bool want_scratch = false;
-    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
-        if (fs_mgr_is_verity_enabled(entry)) {
-            continue;
-        }
-        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) {
-            continue;
-        }
-        want_scratch = true;
-        break;
-    }
-    if (!want_scratch) {
-        return;
-    }
-
     if (ScratchIsOnData()) {
         if (auto images = IImageManager::Open("remount", 0ms)) {
             images->MapAllImages(init);
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
index 5349e41..fd7416b 100644
--- a/fs_mgr/liblp/super_layout_builder.cpp
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -17,6 +17,8 @@
 
 #include <liblp/liblp.h>
 
+#include <algorithm>
+
 #include "images.h"
 #include "utility.h"
 #include "writer.h"
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index d8e171b..70c7b79 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -25,6 +25,7 @@
 #include <sys/ioctl.h>
 #endif
 
+#include <algorithm>
 #include <map>
 #include <string>
 #include <vector>
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 5e5546d..6b34152 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -16,8 +16,10 @@
 
 #include <stdint.h>
 
+#include <limits>
 #include <optional>
 #include <string_view>
+#include <type_traits>
 
 namespace android {
 namespace snapshot {
@@ -32,7 +34,7 @@
 static constexpr uint32_t kCowVersionManifest = 2;
 
 static constexpr uint32_t kMinCowVersion = 1;
-static constexpr uint32_t kMaxCowVersion = 2;
+static constexpr uint32_t kMaxCowVersion = 3;
 
 // Normally, this should be kMaxCowVersion. When a new version is under testing
 // it may be the previous value of kMaxCowVersion.
@@ -119,10 +121,30 @@
     uint32_t compression_algorithm;
 } __attribute__((packed));
 
+enum class CowOperationType : uint8_t {
+    kCowCopyOp = 1,
+    kCowReplaceOp = 2,
+    kCowZeroOp = 3,
+    kCowLabelOp = 4,
+    kCowClusterOp = 5,
+    kCowXorOp = 6,
+    kCowSequenceOp = 7,
+    kCowFooterOp = std::numeric_limits<uint8_t>::max(),
+};
+
+static constexpr CowOperationType kCowCopyOp = CowOperationType::kCowCopyOp;
+static constexpr CowOperationType kCowReplaceOp = CowOperationType::kCowReplaceOp;
+static constexpr CowOperationType kCowZeroOp = CowOperationType::kCowZeroOp;
+static constexpr CowOperationType kCowLabelOp = CowOperationType::kCowLabelOp;
+static constexpr CowOperationType kCowClusterOp = CowOperationType::kCowClusterOp;
+static constexpr CowOperationType kCowXorOp = CowOperationType::kCowXorOp;
+static constexpr CowOperationType kCowSequenceOp = CowOperationType::kCowSequenceOp;
+static constexpr CowOperationType kCowFooterOp = CowOperationType::kCowFooterOp;
+
 // This structure is the same size of a normal Operation, but is repurposed for the footer.
 struct CowFooterOperation {
     // The operation code (always kCowFooterOp).
-    uint8_t type;
+    CowOperationType type;
 
     // If this operation reads from the data section of the COW, this contains
     // the compression type of that data (see constants below).
@@ -141,7 +163,7 @@
 // V2 version of COW. On disk format for older devices
 struct CowOperationV2 {
     // The operation code (see the constants and structures below).
-    uint8_t type;
+    CowOperationType type;
 
     // If this operation reads from the data section of the COW, this contains
     // the compression type of that data (see constants below).
@@ -173,11 +195,12 @@
     uint64_t source;
 } __attribute__((packed));
 
+static constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;
+static constexpr uint64_t kCowOpSourceInfoTypeBit = 60;
+static constexpr uint64_t kCowOpSourceInfoTypeNumBits = 4;
+static constexpr uint64_t kCowOpSourceInfoTypeMask = (1ULL << kCowOpSourceInfoTypeNumBits) - 1;
 // The on disk format of cow (currently ==  CowOperation)
 struct CowOperationV3 {
-    // The operation code (see the constants and structures below).
-    uint8_t type;
-
     // If this operation reads from the data section of the COW, this contains
     // the length.
     uint16_t data_length;
@@ -185,6 +208,10 @@
     // The block of data in the new image that this operation modifies.
     uint32_t new_block;
 
+    // source_info with have the following layout
+    // |---4 bits ---| ---12 bits---| --- 48 bits ---|
+    // |--- type --- | -- unused -- | --- source --- |
+    //
     // The value of |source| depends on the operation code.
     //
     // CopyOp: a 32-bit block location in the source image.
@@ -196,20 +223,34 @@
     // For ops other than Label:
     //  Bits 47-62 are reserved and must be zero.
     // A block is compressed if it’s data is < block_sz
-    uint64_t source_info;
+    uint64_t source_info_;
+    constexpr uint64_t source() const { return source_info_ & kCowOpSourceInfoDataMask; }
+    constexpr void set_source(uint64_t source) {
+        // Clear the first 48 bit first
+        source_info_ &= ~kCowOpSourceInfoDataMask;
+        // Set the actual source field
+        source_info_ |= source & kCowOpSourceInfoDataMask;
+    }
+    constexpr CowOperationType type() const {
+        // this is a mask to grab the first 4 bits of a 64 bit integer
+        const auto type = (source_info_ >> kCowOpSourceInfoTypeBit) & kCowOpSourceInfoTypeMask;
+        return static_cast<CowOperationType>(type);
+    }
+    constexpr void set_type(CowOperationType type) {
+        // Clear the top 4 bits first
+        source_info_ &= ((1ULL << kCowOpSourceInfoTypeBit) - 1);
+        // set the actual type bits
+        source_info_ |= (static_cast<uint64_t>(type) & kCowOpSourceInfoTypeMask)
+                        << kCowOpSourceInfoTypeBit;
+    }
 } __attribute__((packed));
 
+// Ensure that getters/setters added to CowOperationV3 does not increases size
+// of CowOperationV3 struct(no virtual method tables added).
+static_assert(std::is_trivially_copyable_v<CowOperationV3>);
+static_assert(std::is_standard_layout_v<CowOperationV3>);
 static_assert(sizeof(CowOperationV2) == sizeof(CowFooterOperation));
 
-static constexpr uint8_t kCowCopyOp = 1;
-static constexpr uint8_t kCowReplaceOp = 2;
-static constexpr uint8_t kCowZeroOp = 3;
-static constexpr uint8_t kCowLabelOp = 4;
-static constexpr uint8_t kCowClusterOp = 5;
-static constexpr uint8_t kCowXorOp = 6;
-static constexpr uint8_t kCowSequenceOp = 7;
-static constexpr uint8_t kCowFooterOp = -1;
-
 enum CowCompressionAlgorithm : uint8_t {
     kCowCompressNone = 0,
     kCowCompressGz = 1,
@@ -226,12 +267,6 @@
 static constexpr uint8_t kCowReadAheadInProgress = 1;
 static constexpr uint8_t kCowReadAheadDone = 2;
 
-static constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;
-
-static inline uint64_t GetCowOpSourceInfoData(const CowOperation& op) {
-    return op.source_info & kCowOpSourceInfoDataMask;
-}
-
 static constexpr off_t GetSequenceOffset(const CowHeaderV3& header) {
     return header.prefix.header_size + header.buffer_size;
 }
@@ -272,7 +307,7 @@
 
 std::ostream& operator<<(std::ostream& os, CowOperationV2 const& arg);
 
-std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
+std::ostream& operator<<(std::ostream& os, CowOperationV3 const& arg);
 
 std::ostream& operator<<(std::ostream& os, ResumePoint const& arg);
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 08a79ba..d102863 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -826,6 +826,9 @@
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
 
+    // Set read-ahead size during OTA
+    void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb);
+
     android::dm::IDeviceMapper& dm_;
     std::unique_ptr<IDeviceInfo> device_;
     std::string metadata_dir_;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index 937065d..2c4d40f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -30,7 +30,7 @@
 
 using android::base::unique_fd;
 
-std::ostream& EmitCowTypeString(std::ostream& os, uint8_t cow_type) {
+std::ostream& EmitCowTypeString(std::ostream& os, CowOperationType cow_type) {
     switch (cow_type) {
         case kCowCopyOp:
             return os << "kCowCopyOp";
@@ -80,22 +80,21 @@
     return os;
 }
 
-std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
+std::ostream& operator<<(std::ostream& os, CowOperationV3 const& op) {
     os << "CowOperation(";
-    EmitCowTypeString(os, op.type);
-    if (op.type == kCowReplaceOp || op.type == kCowXorOp || op.type == kCowSequenceOp) {
+    EmitCowTypeString(os, op.type());
+    if (op.type() == kCowReplaceOp || op.type() == kCowXorOp || op.type() == kCowSequenceOp) {
         os << ", data_length:" << op.data_length;
     }
-    if (op.type != kCowClusterOp && op.type != kCowSequenceOp && op.type != kCowLabelOp) {
+    if (op.type() != kCowClusterOp && op.type() != kCowSequenceOp && op.type() != kCowLabelOp) {
         os << ", new_block:" << op.new_block;
     }
-    if (op.type == kCowXorOp || op.type == kCowReplaceOp || op.type == kCowCopyOp) {
-        os << ", source:" << (op.source_info & kCowOpSourceInfoDataMask);
-    } else if (op.type == kCowClusterOp) {
-        os << ", cluster_data:" << (op.source_info & kCowOpSourceInfoDataMask);
-    } else {
-        os << ", label:0x" << android::base::StringPrintf("%" PRIx64, op.source_info);
+    if (op.type() == kCowXorOp || op.type() == kCowReplaceOp || op.type() == kCowCopyOp) {
+        os << ", source:" << op.source();
+    } else if (op.type() == kCowClusterOp) {
+        os << ", cluster_data:" << op.source();
     }
+    // V3 op stores resume points in header, so CowOp can never be Label.
     os << ")";
     return os;
 }
@@ -126,7 +125,7 @@
 }
 
 bool IsMetadataOp(const CowOperation& op) {
-    switch (op.type) {
+    switch (op.type()) {
         case kCowLabelOp:
         case kCowClusterOp:
         case kCowFooterOp:
@@ -138,7 +137,7 @@
 }
 
 bool IsOrderedOp(const CowOperation& op) {
-    switch (op.type) {
+    switch (op.type()) {
         case kCowCopyOp:
         case kCowXorOp:
             return true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 7b5370c..1b4a971 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -345,7 +345,7 @@
     for (size_t i = 0; i < ops_->size(); i++) {
         auto& current_op = ops_->data()[i];
 
-        if (current_op.type == kCowSequenceOp) {
+        if (current_op.type() == kCowSequenceOp) {
             size_t seq_len = current_op.data_length / sizeof(uint32_t);
 
             merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
@@ -637,11 +637,11 @@
 }
 
 bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read) {
-    switch (op->type) {
+    switch (op->type()) {
         case kCowSequenceOp:
         case kCowReplaceOp:
         case kCowXorOp:
-            return GetRawBytes(GetCowOpSourceInfoData(*op), buffer, len, read);
+            return GetRawBytes(op->source(), buffer, len, read);
         default:
             LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op;
             return false;
@@ -730,10 +730,10 @@
     }
 
     uint64_t offset;
-    if (op->type == kCowXorOp) {
+    if (op->type() == kCowXorOp) {
         offset = xor_data_loc_->at(op->new_block);
     } else {
-        offset = GetCowOpSourceInfoData(*op);
+        offset = op->source();
     }
     if (!decompressor ||
         ((op->data_length == header_.block_size) && (header_.prefix.major_version == 3))) {
@@ -747,12 +747,12 @@
 }
 
 bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) {
-    switch (op->type) {
+    switch (op->type()) {
         case kCowCopyOp:
-            *source_offset = GetCowOpSourceInfoData(*op) * header_.block_size;
+            *source_offset = op->source() * header_.block_size;
             return true;
         case kCowXorOp:
-            *source_offset = GetCowOpSourceInfoData(*op);
+            *source_offset = op->source();
             return true;
         default:
             return false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 993630b..3718851 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -199,7 +199,7 @@
 
         if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
 
-        if ((FLAGS_decompress || extract_to >= 0) && op->type == kCowReplaceOp) {
+        if ((FLAGS_decompress || extract_to >= 0) && op->type() == kCowReplaceOp) {
             if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
                 std::cerr << "Failed to decompress for :" << *op << "\n";
                 success = false;
@@ -213,12 +213,12 @@
                     return false;
                 }
             }
-        } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type != kCowZeroOp) {
+        } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type() != kCowZeroOp) {
             PLOG(ERROR) << "Cannot extract op yet: " << *op;
             return false;
         }
 
-        if (op->type == kCowSequenceOp && FLAGS_show_merge_sequence) {
+        if (op->type() == kCowSequenceOp && FLAGS_show_merge_sequence) {
             size_t read;
             std::vector<uint32_t> merge_op_blocks;
             size_t seq_len = op->data_length / sizeof(uint32_t);
@@ -236,13 +236,13 @@
             }
         }
 
-        if (op->type == kCowCopyOp) {
+        if (op->type() == kCowCopyOp) {
             copy_ops++;
-        } else if (op->type == kCowReplaceOp) {
+        } else if (op->type() == kCowReplaceOp) {
             replace_ops++;
-        } else if (op->type == kCowZeroOp) {
+        } else if (op->type() == kCowZeroOp) {
             zero_ops++;
-        } else if (op->type == kCowXorOp) {
+        } else if (op->type() == kCowXorOp) {
             xor_ops++;
         }
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
index 08a43a4..fe977b7 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -205,7 +205,7 @@
         const auto& v2_op = v2_ops_->at(i);
 
         auto& new_op = out->ops->at(i);
-        new_op.type = v2_op.type;
+        new_op.set_type(v2_op.type);
         new_op.data_length = v2_op.data_length;
 
         if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
@@ -215,7 +215,7 @@
         new_op.new_block = v2_op.new_block;
 
         uint64_t source_info = v2_op.source;
-        if (new_op.type != kCowLabelOp) {
+        if (new_op.type() != kCowLabelOp) {
             source_info &= kCowOpSourceInfoDataMask;
             if (source_info != v2_op.source) {
                 LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
@@ -232,7 +232,7 @@
                 return false;
             }
         }
-        new_op.source_info = source_info;
+        new_op.set_source(source_info);
     }
 
     out->header = header_;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
index 8e0f190..ce68b39 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
@@ -112,7 +112,7 @@
     xor_data_loc_ = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
 
     for (auto op : *ops_) {
-        if (op.type == kCowXorOp) {
+        if (op.type() == kCowXorOp) {
             xor_data_loc_->insert({op.new_block, data_pos});
         }
         data_pos += op.data_length;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
index a3e40d9..4e90a0f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
@@ -147,7 +147,7 @@
         op = ops_[chunk];
     }
 
-    if (!op || op->type == kCowCopyOp) {
+    if (!op || op->type() == kCowCopyOp) {
         borrowed_fd fd = GetSourceFd();
         if (fd < 0) {
             // GetSourceFd sets errno.
@@ -169,15 +169,15 @@
             // ReadFullyAtOffset sets errno.
             return -1;
         }
-    } else if (op->type == kCowZeroOp) {
+    } else if (op->type() == kCowZeroOp) {
         memset(buffer, 0, bytes_to_read);
-    } else if (op->type == kCowReplaceOp) {
+    } else if (op->type() == kCowReplaceOp) {
         if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
             LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
             errno = EIO;
             return -1;
         }
-    } else if (op->type == kCowXorOp) {
+    } else if (op->type() == kCowXorOp) {
         borrowed_fd fd = GetSourceFd();
         if (fd < 0) {
             // GetSourceFd sets errno.
@@ -208,7 +208,7 @@
             ((char*)buffer)[i] ^= data[i];
         }
     } else {
-        LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
+        LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type());
         errno = EINVAL;
         return -1;
     }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 2709059..49d86d8 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -85,10 +85,10 @@
     size_t i = 0;
     while (!iter->AtEnd()) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowCopyOp);
+        ASSERT_EQ(op->type(), kCowCopyOp);
         ASSERT_EQ(op->data_length, 0);
         ASSERT_EQ(op->new_block, 10 + i);
-        ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
+        ASSERT_EQ(op->source(), 1000 + i);
         iter->Next();
         i += 1;
     }
@@ -131,10 +131,10 @@
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowCopyOp);
+    ASSERT_EQ(op->type(), kCowCopyOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 10);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
+    ASSERT_EQ(op->source(), 20);
 
     std::string sink(data.size(), '\0');
 
@@ -142,7 +142,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_EQ(op->data_length, 4096);
     ASSERT_EQ(op->new_block, 50);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -153,19 +153,19 @@
     op = iter->Get();
 
     // Note: the zero operation gets split into two blocks.
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 51);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+    ASSERT_EQ(op->source(), 0);
 
     iter->Next();
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 52);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+    ASSERT_EQ(op->source(), 0);
 
     iter->Next();
     ASSERT_TRUE(iter->AtEnd());
@@ -206,10 +206,10 @@
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowCopyOp);
+    ASSERT_EQ(op->type(), kCowCopyOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 10);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
+    ASSERT_EQ(op->source(), 20);
 
     std::string sink(data.size(), '\0');
 
@@ -217,10 +217,10 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowXorOp);
+    ASSERT_EQ(op->type(), kCowXorOp);
     ASSERT_EQ(op->data_length, 4096);
     ASSERT_EQ(op->new_block, 50);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314);  // 4096 * 24 + 10
+    ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data);
 
@@ -229,19 +229,19 @@
     op = iter->Get();
 
     // Note: the zero operation gets split into two blocks.
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 51);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+    ASSERT_EQ(op->source(), 0);
 
     iter->Next();
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 52);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+    ASSERT_EQ(op->source(), 0);
 
     iter->Next();
     ASSERT_TRUE(iter->AtEnd());
@@ -273,7 +273,7 @@
 
     std::string sink(data.size(), '\0');
 
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_EQ(op->data_length, 56);  // compressed!
     ASSERT_EQ(op->new_block, 50);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -325,16 +325,16 @@
     while (!iter->AtEnd()) {
         auto op = iter->Get();
 
-        if (op->type == kCowXorOp) {
+        if (op->type() == kCowXorOp) {
             total_blocks += 1;
             std::string sink(xor_data.size(), '\0');
             ASSERT_EQ(op->new_block, 50);
-            ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314);  // 4096 * 24 + 10
+            ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10
             ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
             ASSERT_EQ(sink, xor_data);
         }
 
-        if (op->type == kCowReplaceOp) {
+        if (op->type() == kCowReplaceOp) {
             total_blocks += 1;
             if (op->new_block == 100) {
                 data.resize(options.block_size);
@@ -399,7 +399,7 @@
     while (!iter->AtEnd()) {
         auto op = iter->Get();
 
-        if (op->type == kCowReplaceOp) {
+        if (op->type() == kCowReplaceOp) {
             total_blocks += 1;
             if (op->new_block == 50) {
                 data.resize(options.block_size);
@@ -519,7 +519,7 @@
 
     std::string sink(data.size(), '\0');
 
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_EQ(op->data_length, 56);  // compressed!
     ASSERT_EQ(op->new_block, 50);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -529,7 +529,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowClusterOp);
+    ASSERT_EQ(op->type(), kCowClusterOp);
 
     iter->Next();
     ASSERT_FALSE(iter->AtEnd());
@@ -546,7 +546,7 @@
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowClusterOp);
+    ASSERT_EQ(op->type(), kCowClusterOp);
 
     iter->Next();
     ASSERT_TRUE(iter->AtEnd());
@@ -580,7 +580,7 @@
     std::string sink(options.block_size, '\0');
 
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_EQ(op->new_block, 51);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
 }
@@ -653,7 +653,7 @@
 
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data);
 
@@ -663,14 +663,14 @@
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 3);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 3);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data2);
 
@@ -716,14 +716,14 @@
 
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 0);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
 
     iter->Next();
 
@@ -774,8 +774,8 @@
 
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 5);
 
     iter->Next();
     ASSERT_TRUE(iter->AtEnd());
@@ -825,7 +825,7 @@
 
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data.substr(0, options.block_size));
 
@@ -835,7 +835,7 @@
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data.substr(options.block_size, 2 * options.block_size));
 
@@ -843,26 +843,26 @@
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 4);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
 
     iter->Next();
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 5);
 
     iter->Next();
 
@@ -906,7 +906,7 @@
 
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data.substr(0, options.block_size));
 
@@ -914,52 +914,52 @@
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 4);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowClusterOp);
+    ASSERT_EQ(op->type(), kCowClusterOp);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 5);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowCopyOp);
+    ASSERT_EQ(op->type(), kCowCopyOp);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowClusterOp);
+    ASSERT_EQ(op->type(), kCowClusterOp);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 6);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 6);
 
     iter->Next();
 
@@ -1005,14 +1005,14 @@
 
     ASSERT_FALSE(iter->AtEnd());
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 50);
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 50);
 
     iter->Next();
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data2);
 
@@ -1020,7 +1020,7 @@
 
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
-    ASSERT_EQ(op->type, kCowClusterOp);
+    ASSERT_EQ(op->type(), kCowClusterOp);
 
     iter->Next();
 
@@ -1117,12 +1117,12 @@
         num_in_cluster++;
         max_in_cluster = std::max(max_in_cluster, num_in_cluster);
 
-        if (op->type == kCowReplaceOp) {
+        if (op->type() == kCowReplaceOp) {
             num_replace++;
 
             ASSERT_EQ(op->new_block, num_replace);
             ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
-        } else if (op->type == kCowClusterOp) {
+        } else if (op->type() == kCowClusterOp) {
             num_in_cluster = 0;
             num_clusters++;
         }
@@ -1178,12 +1178,12 @@
         num_in_cluster++;
         max_in_cluster = std::max(max_in_cluster, num_in_cluster);
 
-        if (op->type == kCowReplaceOp) {
+        if (op->type() == kCowReplaceOp) {
             num_replace++;
 
             ASSERT_EQ(op->new_block, num_replace);
             ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
-        } else if (op->type == kCowClusterOp) {
+        } else if (op->type() == kCowClusterOp) {
             num_in_cluster = 0;
             num_clusters++;
         }
@@ -1229,12 +1229,12 @@
 
         num_in_cluster++;
         max_in_cluster = std::max(max_in_cluster, num_in_cluster);
-        if (op->type == kCowReplaceOp) {
+        if (op->type() == kCowReplaceOp) {
             num_replace++;
 
             ASSERT_EQ(op->new_block, num_replace);
             ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
-        } else if (op->type == kCowClusterOp) {
+        } else if (op->type() == kCowClusterOp) {
             num_in_cluster = 0;
             num_clusters++;
         }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index 9ac1448..27accdc 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -15,17 +15,15 @@
 #include <sys/stat.h>
 
 #include <cstdio>
-#include <iostream>
 #include <memory>
-#include <string_view>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
+#include <libsnapshot/cow_format.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
-#include "cow_decompress.h"
-#include "libsnapshot/cow_format.h"
 #include "writer_v2.h"
 #include "writer_v3.h"
 
@@ -128,19 +126,19 @@
     ASSERT_FALSE(iter->AtEnd());
 
     auto op = iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 1);
-    ASSERT_EQ(op->source_info, 0);
+    ASSERT_EQ(op->source(), 0);
 
     iter->Next();
     ASSERT_FALSE(iter->AtEnd());
     op = iter->Get();
 
-    ASSERT_EQ(op->type, kCowZeroOp);
+    ASSERT_EQ(op->type(), kCowZeroOp);
     ASSERT_EQ(op->data_length, 0);
     ASSERT_EQ(op->new_block, 2);
-    ASSERT_EQ(op->source_info, 0);
+    ASSERT_EQ(op->source(), 0);
 }
 
 TEST_F(CowTestV3, ReplaceOp) {
@@ -171,7 +169,7 @@
     auto op = iter->Get();
     std::string sink(data.size(), '\0');
 
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_EQ(op->data_length, 4096);
     ASSERT_EQ(op->new_block, 5);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -211,7 +209,7 @@
 
     while (!iter->AtEnd()) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowReplaceOp);
+        ASSERT_EQ(op->type(), kCowReplaceOp);
         ASSERT_EQ(op->data_length, options.block_size);
         ASSERT_EQ(op->new_block, 5 + i);
         ASSERT_TRUE(
@@ -249,10 +247,10 @@
     size_t i = 0;
     while (!iter->AtEnd()) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowCopyOp);
+        ASSERT_EQ(op->type(), kCowCopyOp);
         ASSERT_EQ(op->data_length, 0);
         ASSERT_EQ(op->new_block, 10 + i);
-        ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
+        ASSERT_EQ(op->source(), 1000 + i);
         iter->Next();
         i += 1;
     }
@@ -285,10 +283,10 @@
     auto op = iter->Get();
     std::string sink(data.size(), '\0');
 
-    ASSERT_EQ(op->type, kCowXorOp);
+    ASSERT_EQ(op->type(), kCowXorOp);
     ASSERT_EQ(op->data_length, 4096);
     ASSERT_EQ(op->new_block, 50);
-    ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314);  // 4096 * 24 + 10
+    ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
     ASSERT_EQ(sink, data);
 }
@@ -325,10 +323,10 @@
 
     while (!iter->AtEnd()) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowXorOp);
+        ASSERT_EQ(op->type(), kCowXorOp);
         ASSERT_EQ(op->data_length, 4096);
         ASSERT_EQ(op->new_block, 50 + i);
-        ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314 + (i * options.block_size));  // 4096 * 24 + 10
+        ASSERT_EQ(op->source(), 98314 + (i * options.block_size));  // 4096 * 24 + 10
         ASSERT_TRUE(
                 ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
         iter->Next();
@@ -378,7 +376,7 @@
 
     while (i < 5) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowZeroOp);
+        ASSERT_EQ(op->type(), kCowZeroOp);
         ASSERT_EQ(op->new_block, 10 + i);
         iter->Next();
         i++;
@@ -386,9 +384,9 @@
     i = 0;
     while (i < 5) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowCopyOp);
+        ASSERT_EQ(op->type(), kCowCopyOp);
         ASSERT_EQ(op->new_block, 15 + i);
-        ASSERT_EQ(GetCowOpSourceInfoData(*op), 3 + i);
+        ASSERT_EQ(op->source(), 3 + i);
         iter->Next();
         i++;
     }
@@ -397,7 +395,7 @@
 
     while (i < 5) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowReplaceOp);
+        ASSERT_EQ(op->type(), kCowReplaceOp);
         ASSERT_EQ(op->new_block, 18 + i);
         ASSERT_TRUE(
                 ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
@@ -410,9 +408,9 @@
     std::fill(sink.begin(), sink.end(), '\0');
     while (i < 5) {
         auto op = iter->Get();
-        ASSERT_EQ(op->type, kCowXorOp);
+        ASSERT_EQ(op->type(), kCowXorOp);
         ASSERT_EQ(op->new_block, 50 + i);
-        ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314 + (i * options.block_size));  // 4096 * 24 + 10
+        ASSERT_EQ(op->source(), 98314 + (i * options.block_size));  // 4096 * 24 + 10
         ASSERT_TRUE(
                 ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
         iter->Next();
@@ -448,7 +446,7 @@
 
     std::string sink(data.size(), '\0');
 
-    ASSERT_EQ(op->type, kCowReplaceOp);
+    ASSERT_EQ(op->type(), kCowReplaceOp);
     ASSERT_EQ(op->data_length, 56);  // compressed!
     ASSERT_EQ(op->new_block, 50);
     ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -621,5 +619,57 @@
     ASSERT_EQ(expected_block, 0);
     ASSERT_TRUE(iter->AtEnd());
 }
+
+TEST_F(CowTestV3, SetSourceManyTimes) {
+    CowOperationV3 op{};
+    op.set_source(1);
+    ASSERT_EQ(op.source(), 1);
+    op.set_source(2);
+    ASSERT_EQ(op.source(), 2);
+    op.set_source(4);
+    ASSERT_EQ(op.source(), 4);
+    op.set_source(8);
+    ASSERT_EQ(op.source(), 8);
+}
+
+TEST_F(CowTestV3, SetTypeManyTimes) {
+    CowOperationV3 op{};
+    op.set_type(kCowCopyOp);
+    ASSERT_EQ(op.type(), kCowCopyOp);
+    op.set_type(kCowReplaceOp);
+    ASSERT_EQ(op.type(), kCowReplaceOp);
+    op.set_type(kCowZeroOp);
+    ASSERT_EQ(op.type(), kCowZeroOp);
+    op.set_type(kCowXorOp);
+    ASSERT_EQ(op.type(), kCowXorOp);
+}
+
+TEST_F(CowTestV3, SetTypeSourceInverleave) {
+    CowOperationV3 op{};
+    op.set_type(kCowCopyOp);
+    ASSERT_EQ(op.type(), kCowCopyOp);
+    op.set_source(0x010203040506);
+    ASSERT_EQ(op.source(), 0x010203040506);
+    ASSERT_EQ(op.type(), kCowCopyOp);
+    op.set_type(kCowReplaceOp);
+    ASSERT_EQ(op.source(), 0x010203040506);
+    ASSERT_EQ(op.type(), kCowReplaceOp);
+}
+
+TEST_F(CowTestV3, CowSizeEstimate) {
+    CowOptions options{};
+    options.compression = "none";
+    auto estimator = android::snapshot::CreateCowEstimator(3, options);
+    ASSERT_TRUE(estimator->AddZeroBlocks(0, 1024 * 1024));
+    const auto cow_size = estimator->GetCowSize();
+    options.op_count_max = 1024 * 1024;
+    options.max_blocks = 1024 * 1024;
+    CowWriterV3 writer(options, GetCowFd());
+    ASSERT_TRUE(writer.Initialize());
+    ASSERT_TRUE(writer.AddZeroBlocks(0, 1024 * 1024));
+
+    ASSERT_LE(writer.GetCowSize(), cow_size);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 37324c7..f9a4e47 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -369,7 +369,7 @@
 }
 
 bool CowWriterV2::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
-                             uint64_t old_block, uint16_t offset, uint8_t type) {
+                             uint64_t old_block, uint16_t offset, CowOperationType type) {
     CHECK(!merge_in_progress_);
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
index 24170eb..50e635f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -42,7 +42,7 @@
     bool EmitCluster();
     bool EmitClusterIfNeeded();
     bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
-                    uint16_t offset, uint8_t type);
+                    uint16_t offset, CowOperationType type);
     void SetupHeaders();
     void SetupWriteOptions();
     bool ParseOptions();
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index b36c6f3..07f6f00 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -176,7 +176,7 @@
 }
 
 bool CowWriterV3::OpenForAppend(uint64_t label) {
-    CowHeaderV3 header_v3;
+    CowHeaderV3 header_v3{};
     if (!ReadCowHeader(fd_, &header_v3)) {
         LOG(ERROR) << "Couldn't read Cow Header";
         return false;
@@ -208,10 +208,10 @@
 
 bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
     for (size_t i = 0; i < num_blocks; i++) {
-        CowOperationV3 op = {};
-        op.type = kCowCopyOp;
+        CowOperationV3 op{};
+        op.set_type(kCowCopyOp);
         op.new_block = new_block + i;
-        op.source_info = old_block + i;
+        op.set_source(old_block + i);
         if (!WriteOperation(op)) {
             return false;
         }
@@ -230,7 +230,7 @@
 }
 
 bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
-                             uint64_t old_block, uint16_t offset, uint8_t type) {
+                             uint64_t old_block, uint16_t offset, CowOperationType type) {
     const size_t num_blocks = (size / header_.block_size);
     for (size_t i = 0; i < num_blocks; i++) {
         const uint8_t* const iter =
@@ -239,11 +239,11 @@
         CowOperation op = {};
         op.new_block = new_block_start + i;
 
-        op.type = type;
+        op.set_type(type);
         if (type == kCowXorOp) {
-            op.source_info = (old_block + i) * header_.block_size + offset;
+            op.set_source((old_block + i) * header_.block_size + offset);
         } else {
-            op.source_info = next_data_pos_;
+            op.set_source(next_data_pos_);
         }
         std::basic_string<uint8_t> compressed_data;
         const void* out_data = iter;
@@ -273,11 +273,9 @@
 
 bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
     for (uint64_t i = 0; i < num_blocks; i++) {
-        CowOperationV3 op;
-        op.type = kCowZeroOp;
-        op.data_length = 0;
+        CowOperationV3 op{};
+        op.set_type(kCowZeroOp);
         op.new_block = new_block_start + i;
-        op.source_info = 0;
         if (!WriteOperation(op)) {
             return false;
         }
@@ -329,7 +327,12 @@
 bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
     if (IsEstimating()) {
         header_.op_count++;
-        header_.op_count_max++;
+        if (header_.op_count > header_.op_count_max) {
+            // If we increment op_count_max, the offset of data section would
+            // change. So need to update |next_data_pos_|
+            next_data_pos_ += (header_.op_count - header_.op_count_max) * sizeof(CowOperationV3);
+            header_.op_count_max = header_.op_count;
+        }
         next_data_pos_ += op.data_length;
         return true;
     }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index 3dfc33c..340218f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -46,7 +46,7 @@
     bool OpenForAppend(uint64_t label);
     bool WriteOperation(const CowOperationV3& op, const void* data = nullptr, size_t size = 0);
     bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
-                    uint16_t offset, uint8_t type);
+                    uint16_t offset, CowOperationType type);
     bool CompressBlocks(size_t num_blocks, const void* data);
 
   private:
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index e91e3b7..f6a35a8 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -30,6 +30,7 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
@@ -82,12 +83,28 @@
 using std::chrono::duration_cast;
 using namespace std::chrono_literals;
 using namespace std::string_literals;
+using android::base::Realpath;
+using android::base::StringPrintf;
 
 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;
+/*
+ * The readahead size is set to 32kb so that
+ * there is no significant memory pressure (/proc/pressure/memory) during boot.
+ * After OTA, during boot, partitions are scanned before marking slot as successful.
+ * This scan will trigger readahead both on source and COW block device thereby
+ * leading to Inactive(file) pages to be very high.
+ *
+ * A lower value may help reduce memory pressure further, however, that will
+ * increase the boot time. Thus, for device which don't care about OTA boot
+ * time, they could use O_DIRECT functionality wherein the I/O to the source
+ * block device will be O_DIRECT.
+ */
+static constexpr auto kCowReadAheadSizeKb = 32;
+static constexpr auto kSourceReadAheadSizeKb = 32;
 
 // Note: IImageManager is an incomplete type in the header, so the default
 // destructor doesn't work.
@@ -1748,6 +1765,9 @@
                 snapuserd_argv->emplace_back(std::move(message));
             }
 
+            SetReadAheadSize(cow_image_device, kCowReadAheadSizeKb);
+            SetReadAheadSize(source_device, kSourceReadAheadSizeKb);
+
             // Do not attempt to connect to the new snapuserd yet, it hasn't
             // been started. We do however want to wait for the misc device
             // to have been created.
@@ -3642,6 +3662,9 @@
     cow_options.max_blocks = {status.device_size() / cow_options.block_size};
     cow_options.batch_write = status.batched_writes();
     cow_options.num_compress_threads = status.enable_threading() ? 2 : 0;
+    // TODO(b/313962438) Improve op_count estimate. For now, use number of
+    // blocks as an upper bound.
+    cow_options.op_count_max = status.device_size() / cow_options.block_size;
     // Disable scratch space for vts tests
     if (device()->IsTestDevice()) {
         cow_options.scratch_space = false;
@@ -4404,5 +4427,31 @@
     return true;
 }
 
+void SnapshotManager::SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {
+    std::string block_device;
+    if (!Realpath(entry_block_device, &block_device)) {
+        PLOG(ERROR) << "Failed to realpath " << entry_block_device;
+        return;
+    }
+
+    static constexpr std::string_view kDevBlockPrefix("/dev/block/");
+    if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {
+        LOG(ERROR) << block_device << " is not a block device";
+        return;
+    }
+
+    std::string block_name = block_device.substr(kDevBlockPrefix.length());
+    std::string sys_partition =
+            android::base::StringPrintf("/sys/class/block/%s/partition", block_name.c_str());
+    struct stat info;
+    if (lstat(sys_partition.c_str(), &info) == 0) {
+        block_name += "/..";
+    }
+    std::string sys_ra = android::base::StringPrintf("/sys/class/block/%s/queue/read_ahead_kb",
+                                                     block_name.c_str());
+    std::string size = std::to_string(size_kb);
+    android::base::WriteStringToFile(size, sys_ra.c_str());
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index ebaca2d..0396a55 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -227,8 +227,12 @@
         if (file_offset >= dev_sz) {
             break;
         }
+
+        if (fsync(cfd.get()) < 0) {
+            PLOG(ERROR) << "Fsync failed at offset: " << file_offset << " size: " << to_read;
+            return false;
+        }
     }
-    fsync(cfd.get());
     return true;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 6dc082e..93bb0b2 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -406,9 +406,9 @@
             break;
         }
 
-        if (cow_op->type == kCowReplaceOp) {
+        if (cow_op->type() == kCowReplaceOp) {
             replace_ops++;
-        } else if (cow_op->type == kCowZeroOp) {
+        } else if (cow_op->type() == kCowZeroOp) {
             zero_ops++;
         }
 
@@ -508,7 +508,7 @@
             // the merge of operations are done based on the ops present
             // in the file.
             //===========================================================
-            uint64_t block_source = GetCowOpSourceInfoData(*cow_op);
+            uint64_t block_source = cow_op->source();
             if (prev_id.has_value()) {
                 if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
                     break;
@@ -540,7 +540,7 @@
             chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
             offset += sizeof(struct disk_exception);
             num_ops += 1;
-            if (cow_op->type == kCowCopyOp) {
+            if (cow_op->type() == kCowCopyOp) {
                 copy_ops++;
             }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
index ab0b309..f1b9245 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
@@ -172,7 +172,7 @@
 }
 
 void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
-    uint64_t source_block = GetCowOpSourceInfoData(*cow_op);
+    uint64_t source_block = cow_op->source();
     if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
         overlap_ = true;
     }
@@ -191,8 +191,8 @@
         // Get the first block with offset
         const CowOperation* cow_op = GetRAOpIter();
         CHECK_NE(cow_op, nullptr);
-        *source_offset = GetCowOpSourceInfoData(*cow_op);
-        if (cow_op->type == kCowCopyOp) {
+        *source_offset = cow_op->source();
+        if (cow_op->type() == kCowCopyOp) {
             *source_offset *= BLOCK_SZ;
         }
         RAIterNext();
@@ -210,8 +210,8 @@
         while (!RAIterDone() && num_ops) {
             const CowOperation* op = GetRAOpIter();
             CHECK_NE(op, nullptr);
-            uint64_t next_offset = GetCowOpSourceInfoData(*op);
-            if (op->type == kCowCopyOp) {
+            uint64_t next_offset = op->source();
+            if (op->type() == kCowCopyOp) {
                 next_offset *= BLOCK_SZ;
             }
             if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
index 571b352..1f5d568 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -177,7 +177,7 @@
         return false;
     }
 
-    switch (cow_op->type) {
+    switch (cow_op->type()) {
         case kCowReplaceOp: {
             return ProcessReplaceOp(cow_op);
         }
@@ -191,7 +191,8 @@
         }
 
         default: {
-            SNAP_LOG(ERROR) << "Unsupported operation-type found: " << cow_op->type;
+            SNAP_LOG(ERROR) << "Unsupported operation-type found: "
+                            << static_cast<uint8_t>(cow_op->type());
         }
     }
     return false;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
index c5718d5..c85331b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -41,7 +41,7 @@
 bool Extractor::Init() {
     auto opener = factory_.CreateTestOpener(control_name_);
     handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
-                                                 opener, 1, false, false);
+                                                 opener, 1, false, false, false);
     if (!handler_->InitCowDevice()) {
         return false;
     }
@@ -50,7 +50,7 @@
     }
 
     read_worker_ = std::make_unique<ReadWorker>(cow_path_, base_path_, control_name_, base_path_,
-                                                handler_->GetSharedPtr(), opener);
+                                                handler_->GetSharedPtr(), opener, false);
     if (!read_worker_->Init()) {
         return false;
     }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
index ffd7a4b..711e704 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -51,10 +51,11 @@
 std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
         const std::string& misc_name, const std::string& cow_device_path,
         const std::string& backing_device, const std::string& base_path_merge,
-        std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring) {
-    auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
-                                                       base_path_merge, opener, num_worker_threads,
-                                                       use_iouring, perform_verification_);
+        std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
+        bool o_direct) {
+    auto snapuserd = std::make_shared<SnapshotHandler>(
+            misc_name, cow_device_path, backing_device, base_path_merge, opener, num_worker_threads,
+            use_iouring, perform_verification_, o_direct);
     if (!snapuserd->InitCowDevice()) {
         LOG(ERROR) << "Failed to initialize Snapuserd";
         return nullptr;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
index ff6ee8f..f23f07e 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -57,7 +57,8 @@
                                                       const std::string& backing_device,
                                                       const std::string& base_path_merge,
                                                       std::shared_ptr<IBlockServerOpener> opener,
-                                                      int num_worker_threads, bool use_iouring) = 0;
+                                                      int num_worker_threads, bool use_iouring,
+                                                      bool o_direct) = 0;
 
     // Start serving requests on a snapshot handler.
     virtual bool StartHandler(const std::string& misc_name) = 0;
@@ -96,7 +97,8 @@
                                               const std::string& backing_device,
                                               const std::string& base_path_merge,
                                               std::shared_ptr<IBlockServerOpener> opener,
-                                              int num_worker_threads, bool use_iouring) override;
+                                              int num_worker_threads, bool use_iouring,
+                                              bool o_direct) override;
     bool StartHandler(const std::string& misc_name) override;
     bool DeleteHandler(const std::string& misc_name) override;
     bool InitiateMerge(const std::string& misc_name) override;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index 11b8d7c..bcf9aab 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -114,13 +114,13 @@
                 SNAP_LOG(ERROR) << "AcquireBuffer failed in MergeReplaceOps";
                 return false;
             }
-            if (cow_op->type == kCowReplaceOp) {
+            if (cow_op->type() == kCowReplaceOp) {
                 if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
                     SNAP_LOG(ERROR) << "Failed to read COW in merge";
                     return false;
                 }
             } else {
-                CHECK(cow_op->type == kCowZeroOp);
+                CHECK(cow_op->type() == kCowZeroOp);
                 memset(buffer, 0, BLOCK_SZ);
             }
         }
@@ -557,7 +557,7 @@
         return true;
     }
 
-    if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+    if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
         SNAP_PLOG(ERROR) << "Failed to set thread priority";
     }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
index 5cb13e8..f1d4065 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -31,16 +31,19 @@
 void ReadWorker::CloseFds() {
     block_server_ = {};
     backing_store_fd_ = {};
+    backing_store_direct_fd_ = {};
     Worker::CloseFds();
 }
 
 ReadWorker::ReadWorker(const std::string& cow_device, const std::string& backing_device,
                        const std::string& misc_name, const std::string& base_path_merge,
                        std::shared_ptr<SnapshotHandler> snapuserd,
-                       std::shared_ptr<IBlockServerOpener> opener)
+                       std::shared_ptr<IBlockServerOpener> opener, bool direct_read)
     : Worker(cow_device, misc_name, base_path_merge, snapuserd),
       backing_store_device_(backing_device),
-      block_server_opener_(opener) {}
+      direct_read_(direct_read),
+      block_server_opener_(opener),
+      aligned_buffer_(std::unique_ptr<void, decltype(&::free)>(nullptr, &::free)) {}
 
 // Start the replace operation. This will read the
 // internal COW format and if the block is compressed,
@@ -61,9 +64,20 @@
     }
     SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
                     << " Op: " << *cow_op;
+
+    if (direct_read_ && IsBlockAligned(offset)) {
+        if (!android::base::ReadFullyAtOffset(backing_store_direct_fd_, aligned_buffer_.get(),
+                                              BLOCK_SZ, offset)) {
+            SNAP_PLOG(ERROR) << "O_DIRECT Read failed at offset: " << offset;
+            return false;
+        }
+        std::memcpy(buffer, aligned_buffer_.get(), BLOCK_SZ);
+        return true;
+    }
+
     if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
         std::string op;
-        if (cow_op->type == kCowCopyOp)
+        if (cow_op->type() == kCowCopyOp)
             op = "Copy-op";
         else {
             op = "Xor-op";
@@ -133,7 +147,7 @@
         }
         case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
             bool ret;
-            if (cow_op->type == kCowCopyOp) {
+            if (cow_op->type() == kCowCopyOp) {
                 ret = ProcessCopyOp(cow_op, buffer);
             } else {
                 ret = ProcessXorOp(cow_op, buffer);
@@ -167,7 +181,7 @@
         return false;
     }
 
-    switch (cow_op->type) {
+    switch (cow_op->type()) {
         case kCowReplaceOp: {
             return ProcessReplaceOp(cow_op, buffer);
         }
@@ -183,7 +197,8 @@
         }
 
         default: {
-            SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+            SNAP_LOG(ERROR) << "Unknown operation-type found: "
+                            << static_cast<uint8_t>(cow_op->type());
         }
     }
     return false;
@@ -200,6 +215,24 @@
         return false;
     }
 
+    if (direct_read_) {
+        backing_store_direct_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY | O_DIRECT));
+        if (backing_store_direct_fd_ < 0) {
+            SNAP_PLOG(ERROR) << "Open Failed with O_DIRECT: " << backing_store_direct_fd_;
+            direct_read_ = false;
+        } else {
+            void* aligned_addr;
+            ssize_t page_size = getpagesize();
+            if (posix_memalign(&aligned_addr, page_size, page_size) < 0) {
+                direct_read_ = false;
+                SNAP_PLOG(ERROR) << "posix_memalign failed "
+                                 << " page_size: " << page_size << " read_sz: " << page_size;
+            } else {
+                aligned_buffer_.reset(aligned_addr);
+            }
+        }
+    }
+
     block_server_ = block_server_opener_->Open(this, PAYLOAD_BUFFER_SZ);
     if (!block_server_) {
         SNAP_PLOG(ERROR) << "Unable to open block server";
@@ -213,7 +246,7 @@
 
     pthread_setname_np(pthread_self(), "ReadWorker");
 
-    if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+    if (!SetThreadPriority(ANDROID_PRIORITY_NORMAL)) {
         SNAP_PLOG(ERROR) << "Failed to set thread priority";
     }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
index 6dbae81..1aff50c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -28,7 +28,7 @@
     ReadWorker(const std::string& cow_device, const std::string& backing_device,
                const std::string& misc_name, const std::string& base_path_merge,
                std::shared_ptr<SnapshotHandler> snapuserd,
-               std::shared_ptr<IBlockServerOpener> opener);
+               std::shared_ptr<IBlockServerOpener> opener, bool direct_read = false);
 
     bool Run();
     bool Init() override;
@@ -59,11 +59,14 @@
 
     std::string backing_store_device_;
     unique_fd backing_store_fd_;
+    unique_fd backing_store_direct_fd_;
+    bool direct_read_ = false;
 
     std::shared_ptr<IBlockServerOpener> block_server_opener_;
     std::unique_ptr<IBlockServer> block_server_;
 
     std::basic_string<uint8_t> xor_buffer_;
+    std::unique_ptr<void, decltype(&::free)> aligned_buffer_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 950d771..05ba047 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -36,7 +36,7 @@
 SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
                                  std::string backing_device, std::string base_path_merge,
                                  std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,
-                                 bool use_iouring, bool perform_verification) {
+                                 bool use_iouring, bool perform_verification, bool o_direct) {
     misc_name_ = std::move(misc_name);
     cow_device_ = std::move(cow_device);
     backing_store_device_ = std::move(backing_device);
@@ -45,13 +45,14 @@
     num_worker_threads_ = num_worker_threads;
     is_io_uring_enabled_ = use_iouring;
     perform_verification_ = perform_verification;
+    o_direct_ = o_direct;
 }
 
 bool SnapshotHandler::InitializeWorkers() {
     for (int i = 0; i < num_worker_threads_; i++) {
         auto wt = std::make_unique<ReadWorker>(cow_device_, backing_store_device_, misc_name_,
                                                base_path_merge_, GetSharedPtr(),
-                                               block_server_opener_);
+                                               block_server_opener_, o_direct_);
         if (!wt->Init()) {
             SNAP_LOG(ERROR) << "Thread initialization failed";
             return false;
@@ -199,13 +200,13 @@
     while (!cowop_iter->AtEnd()) {
         const CowOperation* cow_op = cowop_iter->Get();
 
-        if (cow_op->type == kCowCopyOp) {
+        if (cow_op->type() == kCowCopyOp) {
             copy_ops += 1;
-        } else if (cow_op->type == kCowReplaceOp) {
+        } else if (cow_op->type() == kCowReplaceOp) {
             replace_ops += 1;
-        } else if (cow_op->type == kCowZeroOp) {
+        } else if (cow_op->type() == kCowZeroOp) {
             zero_ops += 1;
-        } else if (cow_op->type == kCowXorOp) {
+        } else if (cow_op->type() == kCowXorOp) {
             xor_ops += 1;
         }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index fa1e7a0..9b7238a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -47,6 +47,7 @@
 #include <snapuserd/snapuserd_buffer.h>
 #include <snapuserd/snapuserd_kernel.h>
 #include <storage_literals/storage_literals.h>
+#include <system/thread_defs.h>
 #include "snapuserd_readahead.h"
 #include "snapuserd_verify.h"
 
@@ -62,8 +63,6 @@
 
 static constexpr int kNumWorkerThreads = 4;
 
-static constexpr int kNiceValueForMergeThreads = -5;
-
 #define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
 #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
 
@@ -105,7 +104,7 @@
   public:
     SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
                     std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,
-                    int num_workers, bool use_iouring, bool perform_verification);
+                    int num_workers, bool use_iouring, bool perform_verification, bool o_direct);
     bool InitCowDevice();
     bool Start();
 
@@ -247,6 +246,7 @@
     bool perform_verification_ = true;
     bool resume_merge_ = false;
     bool merge_complete_ = false;
+    bool o_direct_ = false;
 
     std::unique_ptr<UpdateVerify> update_verify_;
     std::shared_ptr<IBlockServerOpener> block_server_opener_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 998d233..c08c1b1 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -77,7 +77,7 @@
         SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op;
         return nr_consecutive;
     }
-    if (cow_op->type == kCowXorOp) {
+    if (cow_op->type() == kCowXorOp) {
         xor_op_vec.push_back(cow_op);
     }
 
@@ -106,7 +106,7 @@
             break;
         }
 
-        if (op->type == kCowXorOp) {
+        if (op->type() == kCowXorOp) {
             xor_op_vec.push_back(op);
         }
 
@@ -778,7 +778,7 @@
 
     InitializeIouring();
 
-    if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+    if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
         SNAP_PLOG(ERROR) << "Failed to set thread priority";
     }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 6eee357..0b881b6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -346,7 +346,8 @@
 std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(const std::string& misc_name,
                                                               const std::string& cow_device_path,
                                                               const std::string& backing_device,
-                                                              const std::string& base_path_merge) {
+                                                              const std::string& base_path_merge,
+                                                              const bool o_direct) {
     // We will need multiple worker threads only during
     // device boot after OTA. For all other purposes,
     // one thread is sufficient. We don't want to consume
@@ -368,7 +369,7 @@
     auto opener = block_server_factory_->CreateOpener(misc_name);
 
     return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
-                                 opener, num_worker_threads, io_uring_enabled_);
+                                 opener, num_worker_threads, io_uring_enabled_, o_direct);
 }
 
 bool UserSnapshotServer::WaitForSocket() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index 9926071..3013c47 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -86,7 +86,8 @@
     std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
                                               const std::string& cow_device_path,
                                               const std::string& backing_device,
-                                              const std::string& base_path_merge);
+                                              const std::string& base_path_merge,
+                                              bool o_direct = false);
     bool StartHandler(const std::string& misc_name);
 
     void SetTerminating() { terminating_ = true; }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 73c3cbf..8ddb0f4 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -59,10 +59,16 @@
 using testing::AssertionFailure;
 using testing::AssertionResult;
 using testing::AssertionSuccess;
+using ::testing::TestWithParam;
 
-class SnapuserdTestBase : public ::testing::TestWithParam<bool> {
+struct TestParam {
+    bool io_uring;
+    bool o_direct;
+};
+
+class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {
   protected:
-    void SetUp() override;
+    virtual void SetUp() override;
     void TearDown() override;
     void CreateBaseDevice();
     void CreateCowDevice();
@@ -628,9 +634,10 @@
     auto factory = harness_->GetBlockServerFactory();
     auto opener = factory->CreateOpener(system_device_ctrl_name_);
     handlers_->DisableVerification();
-    auto handler =
-            handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
-                                  base_dev_->GetPath(), opener, 1, GetParam());
+    const TestParam params = GetParam();
+    auto handler = handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path,
+                                         base_dev_->GetPath(), base_dev_->GetPath(), opener, 1,
+                                         params.io_uring, params.o_direct);
     ASSERT_NE(handler, nullptr);
     ASSERT_NE(handler->snapuserd(), nullptr);
 #ifdef __ANDROID__
@@ -898,9 +905,10 @@
     opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);
     ASSERT_NE(opener_, nullptr);
 
+    const TestParam params = GetParam();
     handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
                                                  base_dev_->GetPath(), base_dev_->GetPath(),
-                                                 opener_, 1, false, false);
+                                                 opener_, 1, false, false, params.o_direct);
     ASSERT_TRUE(handler_->InitCowDevice());
     ASSERT_TRUE(handler_->InitializeWorkers());
 
@@ -990,14 +998,28 @@
     return {false, true};
 }
 
-std::string IoUringConfigName(const testing::TestParamInfo<SnapuserdTest::ParamType>& info) {
-    return info.param ? "io_uring" : "sync";
+std::vector<TestParam> GetTestConfigs() {
+    std::vector<TestParam> testParams;
+    std::vector<bool> uring_configs = GetIoUringConfigs();
+
+    for (bool config : uring_configs) {
+        TestParam param;
+        param.io_uring = config;
+        param.o_direct = false;
+        testParams.push_back(std::move(param));
+    }
+
+    for (bool config : uring_configs) {
+        TestParam param;
+        param.io_uring = config;
+        param.o_direct = true;
+        testParams.push_back(std::move(param));
+    }
+    return testParams;
 }
 
-INSTANTIATE_TEST_SUITE_P(Io, SnapuserdTest, ::testing::ValuesIn(GetIoUringConfigs()),
-                         IoUringConfigName);
-INSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetIoUringConfigs()),
-                         IoUringConfigName);
+INSTANTIATE_TEST_SUITE_P(Io, SnapuserdTest, ::testing::ValuesIn(GetTestConfigs()));
+INSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetTestConfigs()));
 
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
index d07d2f8..7c99085 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
@@ -48,10 +48,27 @@
     std::mutex m_lock_;
     std::condition_variable m_cv_;
 
+    /*
+     * Scanning of partitions is an expensive operation both in terms of memory
+     * and CPU usage. The goal here is to scan the partitions fast enough without
+     * significant increase in the boot time.
+     *
+     * Partitions such as system, product which may be huge and may need multiple
+     * threads to speed up the verification process. Using multiple threads for
+     * all partitions may increase CPU usage significantly. Hence, limit that to
+     * 1 thread per partition.
+     *
+     * These numbers were derived by monitoring the memory and CPU pressure
+     * (/proc/pressure/{cpu,memory}; and monitoring the Inactive(file) and
+     * Active(file) pages from /proc/meminfo.
+     *
+     * Additionally, for low memory devices, it is advisible to use O_DIRECT
+     * fucntionality for source block device.
+     */
     int kMinThreadsToVerify = 1;
-    int kMaxThreadsToVerify = 4;
-    uint64_t kThresholdSize = 512_MiB;
-    uint64_t kBlockSizeVerify = 1_MiB;
+    int kMaxThreadsToVerify = 3;
+    uint64_t kThresholdSize = 750_MiB;
+    uint64_t kBlockSizeVerify = 2_MiB;
 
     bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
     void UpdatePartitionVerificationState(UpdateVerifyState state);
diff --git a/init/service_test.cpp b/init/service_test.cpp
index c81b007..a3590b5 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -236,6 +236,11 @@
 // Service::Stop() if their uid_%d/pid_%d cgroup directory got removed. This test, if run with the
 // parameter set to 'true', verifies that such services are stopped.
 TEST_P(ServiceStopTest, stop) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+
     static constexpr std::string_view kServiceName = "ServiceA";
     static constexpr std::string_view kScriptTemplate = R"init(
 service $name /system/bin/yes
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 2c08b0b..f2ef316 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -91,6 +91,11 @@
       "Name": "CfqWeight",
       "Controller": "io",
       "File": "io.weight"
+    },
+    {
+      "Name": "IoPrioClass",
+      "Controller": "io",
+      "File": "io.prio.class"
     }
   ],
 
@@ -479,6 +484,15 @@
             "Value": "200",
             "Optional": "true"
           }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "restrict-to-be",
+            "Optional": "true"
+          }
         }
       ]
     },
@@ -511,6 +525,15 @@
             "Value": "1000",
             "Optional": "true"
           }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "restrict-to-be",
+            "Optional": "true"
+          }
         }
       ]
     },
@@ -543,6 +566,15 @@
             "Value": "1000",
             "Optional": "true"
           }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "promote-to-rt",
+            "Optional": "true"
+          }
         }
       ]
     },
@@ -575,6 +607,15 @@
             "Value": "1000",
             "Optional": "true"
           }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "promote-to-rt",
+            "Optional": "true"
+          }
         }
       ]
     },
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 4d4294b..85a0fd2 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -203,6 +203,7 @@
     defaults: ["libutils_impl_defaults"],
 
     cflags: [
+        "-DDEBUG_CALLBACKS=1",
         "-DDEBUG_POLL_AND_WAKE=1",
         "-DDEBUG_REFS=1",
         "-DDEBUG_TOKENIZER=1",
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 402e43c..576c61d 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -534,7 +534,7 @@
 
 int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
 #if DEBUG_CALLBACKS
-    ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
+    ALOGD("%p ~ removeFd - seq=%" PRIu64, this, seq);
 #endif
 
     const auto& request_it = mRequests.find(seq);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index cc6b64a..7deb173 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -72,6 +72,11 @@
   endif
 endif
 
+EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE :=
+ifneq ($(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE),)
+  EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE := export SCUDO_ALLOCATION_RING_BUFFER_SIZE $(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE)
+endif
+
 EXPORT_GLOBAL_GCOV_OPTIONS :=
 ifeq ($(NATIVE_COVERAGE),true)
   EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
@@ -216,6 +221,7 @@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%?$(EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE)?g' $@
 
 # Append PLATFORM_VNDK_VERSION to base name.
 define append_vndk_version
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index bf6e986..7ba1f46 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -14,3 +14,4 @@
     %EXPORT_GLOBAL_GCOV_OPTIONS%
     %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
     %EXPORT_GLOBAL_HWASAN_OPTIONS%
+    %EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%
diff --git a/trusty/OWNERS b/trusty/OWNERS
index bf16912..4016792 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -2,7 +2,6 @@
 arve@android.com
 danielangell@google.com
 gmar@google.com
-marcone@google.com
 mikemcternan@google.com
 mmaurer@google.com
 ncbray@google.com
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index f782d2a..0915eab 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -107,7 +107,11 @@
         return {};
     }
 
-    assert(st.st_size >= 0);
+    if (st.st_size == 0) {
+        LOG(ERROR) << "Zero length file '" << file_name << "'";
+        return {};
+    }
+
     file_size = st.st_size;
 
     /* The dmabuf size needs to be a multiple of the page size */
@@ -123,7 +127,8 @@
     BufferAllocator alloc;
     unique_fd dmabuf_fd(alloc.Alloc(kDmabufSystemHeapName, file_page_size));
     if (!dmabuf_fd.ok()) {
-        LOG(ERROR) << "Error creating dmabuf: " << dmabuf_fd.get();
+        LOG(ERROR) << "Error creating dmabuf for " << file_page_size
+                   << " bytes: " << dmabuf_fd.get();
         return dmabuf_fd;
     }