Merge "Enable continuous coverage: use '%c' specifier in LLVM_PROFILE_FILE"
diff --git a/diagnose_usb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
index 35edb5e..5716323 100644
--- a/diagnose_usb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -19,7 +19,9 @@
 #include <errno.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <string>
+#include <vector>
 
 #include <android-base/stringprintf.h>
 
@@ -45,9 +47,25 @@
         return "";
     }
 
-    // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
-    // additionally just to be sure.
-    if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
+    int ngroups = getgroups(0, nullptr);
+    if (ngroups < 0) {
+        perror("failed to get groups list size");
+        return "";
+    }
+
+    std::vector<gid_t> groups(ngroups);
+    ngroups = getgroups(groups.size(), groups.data());
+    if (ngroups < 0) {
+        perror("failed to get groups list");
+        return "";
+    }
+
+    groups.resize(ngroups);
+
+    // getgroups(2) indicates that the egid may not be included so we check it additionally just
+    // to be sure.
+    if (std::find(groups.begin(), groups.end(), plugdev_group->gr_gid) != groups.end() ||
+        getegid() == plugdev_group->gr_gid) {
         // The user is in plugdev so the problem is likely with the udev rules.
         return "missing udev rules? user is in the plugdev group";
     }
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 322fe5c..10bed6d 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -23,5 +23,5 @@
 my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
 my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
 my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
-$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
+$(call dist-for-goals,dist_files sdk,$(my_dist_files))
 my_dist_files :=
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 58b2a81..17b3466 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,3 @@
 dvander@google.com
-hridya@google.com
+elsk@google.com
 enh@google.com
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 4042531..b9f6c97 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -268,10 +268,18 @@
     }
 
     // arg[0] is the command name, arg[1] contains size of data to be downloaded
+    // which should always be 8 bytes
+    if (args[1].length() != 8) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Invalid size (length of size != 8)");
+    }
     unsigned int size;
     if (!android::base::ParseUint("0x" + args[1], &size, kMaxDownloadSizeDefault)) {
         return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
     }
+    if (size == 0) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid size (0)");
+    }
     device->download_data().resize(size);
     if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
         return false;
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index e6a834e..ae225de 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -186,6 +186,11 @@
             PLOG(ERROR) << "Couldn't read command";
             return;
         }
+        if (std::count_if(command, command + bytes_read, iscntrl) != 0) {
+            WriteStatus(FastbootResult::FAIL,
+                        "Command contains control character");
+            continue;
+        }
         command[bytes_read] = '\0';
 
         LOG(INFO) << "Fastboot command: " << command;
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 3f9bcdc..7bef72a 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -172,7 +172,8 @@
         return -EOVERFLOW;
     } else if (data.size() < block_device_size &&
                (partition_name == "boot" || partition_name == "boot_a" ||
-                partition_name == "boot_b")) {
+                partition_name == "boot_b" || partition_name == "init_boot" ||
+                partition_name == "init_boot_a" || partition_name == "init_boot_b")) {
         CopyAVBFooter(&data, block_device_size);
     }
     if (android::base::GetProperty("ro.system.build.type", "") != "user") {
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index f5a3384..e9bf9e9 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo modem odm odm_dlkm oem product pvmfw radio recovery system vbmeta vendor vendor_dlkm"
+        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 532b524..af8c502 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -141,6 +141,10 @@
 static Image images[] = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, ImageType::BootCritical },
+    { "init_boot",
+                  "init_boot.img",    "init_boot.sig",
+                                                      "init_boot",
+                                                                  false, ImageType::BootCritical },
     { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  ImageType::Normal },
     { "cache",    "cache.img",        "cache.sig",    "cache",    true,  ImageType::Extra },
     { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  ImageType::BootCritical },
@@ -152,6 +156,10 @@
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  ImageType::BootCritical },
     { "super",    "super.img",        "super.sig",    "super",    true,  ImageType::Extra },
     { "system",   "system.img",       "system.sig",   "system",   false, ImageType::Normal },
+    { "system_dlkm",
+                  "system_dlkm.img",  "system_dlkm.sig",
+                                                      "system_dlkm",
+                                                                  true,  ImageType::Normal },
     { "system_ext",
                   "system_ext.img",   "system_ext.sig",
                                                       "system_ext",
@@ -1017,7 +1025,7 @@
     return partition_size;
 }
 
-static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+static void copy_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
     if (buf->sz < AVB_FOOTER_SIZE) {
         return;
     }
@@ -1032,9 +1040,9 @@
     // In this case, partition_size will be zero.
     if (partition_size < buf->sz) {
         fprintf(stderr,
-                "Warning: skip copying boot image avb footer"
-                " (boot partition size: %" PRId64 ", boot image size: %" PRId64 ").\n",
-                partition_size, buf->sz);
+                "Warning: skip copying %s image avb footer"
+                " (%s partition size: %" PRId64 ", %s image size: %" PRId64 ").\n",
+                partition.c_str(), partition.c_str(), partition_size, partition.c_str(), buf->sz);
         return;
     }
 
@@ -1042,7 +1050,7 @@
     // Because buf->fd will still be used afterwards.
     std::string data;
     if (!android::base::ReadFdToString(buf->fd, &data)) {
-        die("Failed reading from boot");
+        die("Failed reading from %s", partition.c_str());
     }
 
     uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
@@ -1051,13 +1059,14 @@
         return;
     }
 
-    unique_fd fd(make_temporary_fd("boot rewriting"));
+    const std::string tmp_fd_template = partition + " rewriting";
+    unique_fd fd(make_temporary_fd(tmp_fd_template.c_str()));
     if (!android::base::WriteStringToFd(data, fd)) {
-        die("Failed writing to modified boot");
+        die("Failed writing to modified %s", partition.c_str());
     }
     lseek(fd.get(), partition_size - AVB_FOOTER_SIZE, SEEK_SET);
     if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
-        die("Failed copying AVB footer in boot");
+        die("Failed copying AVB footer in %s", partition.c_str());
     }
     buf->fd = std::move(fd);
     buf->sz = partition_size;
@@ -1068,8 +1077,9 @@
 {
     sparse_file** s;
 
-    if (partition == "boot" || partition == "boot_a" || partition == "boot_b") {
-        copy_boot_avb_footer(partition, buf);
+    if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
+        partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b") {
+        copy_avb_footer(partition, buf);
     }
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
diff --git a/fastboot/fuzzy_fastboot/README.md b/fastboot/fuzzy_fastboot/README.md
index 72967c5..a5b64c7 100644
--- a/fastboot/fuzzy_fastboot/README.md
+++ b/fastboot/fuzzy_fastboot/README.md
@@ -293,7 +293,7 @@
 Begin with just the generic tests (i.e. no XML file). In particular, make sure all
 the conformance tests are passing before you move on. All other tests require that
 the basic generic conformance tests all pass for them to be valid. The conformance
-tests can be run with `./fuzzy_fastboot --gtests_filter=Conformance.*`.
+tests can be run with `./fuzzy_fastboot --gtest_filter=Conformance.*`.
 
 #### Understanding and Fixing Failed Tests
 Whenever a test fails, it will print out to the console the reason for failure
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index b6beaf9..8593adc 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -890,28 +890,51 @@
 
 TEST_F(Fuzz, BadCommandTooLarge) {
     std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);
-    EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
+    RetCode ret = fb->RawCommand(s);
+    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
             << "Device did not respond with failure after sending length " << s.size()
             << " string of random ASCII chars";
+    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
     std::string s1 = RandomString(1000, rand_legal);
-    EXPECT_EQ(fb->RawCommand(s1), DEVICE_FAIL)
+    ret = fb->RawCommand(s1);
+    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
             << "Device did not respond with failure after sending length " << s1.size()
             << " string of random ASCII chars";
+    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
     std::string s2 = RandomString(1000, rand_illegal);
-    EXPECT_EQ(fb->RawCommand(s2), DEVICE_FAIL)
-            << "Device did not respond with failure after sending length " << s1.size()
+    ret = fb->RawCommand(s2);
+    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+            << "Device did not respond with failure after sending length " << s2.size()
             << " string of random non-ASCII chars";
+    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
     std::string s3 = RandomString(1000, rand_char);
-    EXPECT_EQ(fb->RawCommand(s3), DEVICE_FAIL)
-            << "Device did not respond with failure after sending length " << s1.size()
+    ret = fb->RawCommand(s3);
+    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+            << "Device did not respond with failure after sending length " << s3.size()
             << " string of random chars";
+    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+
+    std::string s4 = RandomString(10 * 1024 * 1024, rand_legal);
+    ret = fb->RawCommand(s);
+    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+            << "Device did not respond with failure after sending length " << s4.size()
+            << " string of random ASCII chars ";
+    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+    std::string resp;
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+                << "Device is unresponsive to getvar command";
 }
 
 TEST_F(Fuzz, CommandTooLarge) {
     for (const std::string& s : CMDS) {
         std::string rs = RandomString(1000, rand_char);
-        EXPECT_EQ(fb->RawCommand(s + rs), DEVICE_FAIL)
-                << "Device did not respond with failure after '" << s + rs << "'";
+        RetCode ret;
+        ret = fb->RawCommand(s + rs);
+        EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+                << "Device did not respond with failure " << ret << "after '" << s + rs << "'";
+        if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
         ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
         std::string resp;
         EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 5872dda..49761ac 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -69,7 +69,6 @@
         "file_wait.cpp",
         "fs_mgr.cpp",
         "fs_mgr_format.cpp",
-        "fs_mgr_verity.cpp",
         "fs_mgr_dm_linear.cpp",
         "fs_mgr_overlayfs.cpp",
         "fs_mgr_roots.cpp",
diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp
index 14b217c..388dadc 100644
--- a/fs_mgr/blockdev.cpp
+++ b/fs_mgr/blockdev.cpp
@@ -30,7 +30,6 @@
 using android::base::ErrnoError;
 using android::base::Error;
 using android::base::Result;
-using android::base::ResultError;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -94,10 +93,8 @@
     std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
     LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
     if (blockdev.empty()) {
-        const std::string err_msg =
-                StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev),
-                             minor(statbuf.st_dev), file_path.c_str());
-        return ResultError(err_msg, 0);
+        return Errorf("Failed to convert {}:{} (path {})", major(statbuf.st_dev),
+                      minor(statbuf.st_dev), file_path.c_str());
     }
     auto& dm = DeviceMapper::Instance();
     for (;;) {
@@ -110,7 +107,7 @@
     }
     std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
     if (!maybe_blockdev) {
-        return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
+        return Errorf("Failed to remove /dev/block/ prefix from {}", blockdev);
     }
     blockdev = PartitionParent(*maybe_blockdev);
     LOG(DEBUG) << __func__ << ": "
@@ -119,7 +116,7 @@
             StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
     std::string nr_tags;
     if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
-        return ResultError("Failed to read " + nr_tags_path, 0);
+        return Errorf("Failed to read {}", nr_tags_path);
     }
     rtrim(nr_tags);
     LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
@@ -137,11 +134,9 @@
 
     const std::string loop_device_name = Basename(loop_device_path);
 
-    const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
+    const auto qd = BlockDeviceQueueDepth(file_path);
     if (!qd.ok()) {
-        LOG(DEBUG) << __func__ << ": "
-                   << "BlockDeviceQueueDepth() returned " << qd.error();
-        return ResultError(qd.error());
+        return qd.error();
     }
     const std::string nr_requests = StringPrintf("%u", *qd);
     const std::string sysfs_path =
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a320c0e..33dca58 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1443,14 +1443,6 @@
                 // Skips mounting the device.
                 continue;
             }
-        } else if ((current_entry.fs_mgr_flags.verify)) {
-            int rc = fs_mgr_setup_verity(&current_entry, true);
-            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
-                LINFO << "Verity disabled";
-            } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
-                LERROR << "Could not set up verified partition, skipping!";
-                continue;
-            }
         }
 
         int last_idx_inspected;
@@ -1615,13 +1607,6 @@
                 ret |= FsMgrUmountStatus::ERROR_VERITY;
                 continue;
             }
-        } else if ((current_entry.fs_mgr_flags.verify)) {
-            if (!fs_mgr_teardown_verity(&current_entry)) {
-                LERROR << "Failed to tear down verified partition on mount point: "
-                       << current_entry.mount_point;
-                ret |= FsMgrUmountStatus::ERROR_VERITY;
-                continue;
-            }
         }
     }
     return ret;
@@ -1914,14 +1899,6 @@
                 // Skips mounting the device.
                 continue;
             }
-        } else if (fstab_entry.fs_mgr_flags.verify) {
-            int rc = fs_mgr_setup_verity(&fstab_entry, true);
-            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
-                LINFO << "Verity disabled";
-            } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
-                LERROR << "Could not set up verified partition, skipping!";
-                continue;
-            }
         }
 
         int retry_count = 2;
@@ -2044,7 +2021,9 @@
 
     ConfigureIoScheduler(loop_device);
 
-    ConfigureQueueDepth(loop_device, "/");
+    if (auto ret = ConfigureQueueDepth(loop_device, "/"); !ret.ok()) {
+        LOG(DEBUG) << "Failed to config queue depth: " << ret.error().message();
+    }
 
     // set block size & direct IO
     unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
@@ -2138,7 +2117,7 @@
 }
 
 bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+    if (!entry.fs_mgr_flags.avb) {
         return false;
     }
 
@@ -2149,17 +2128,12 @@
         return false;
     }
 
-    const char* status;
     std::vector<DeviceMapper::TargetInfo> table;
     if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
-        if (!entry.fs_mgr_flags.verify_at_boot) {
-            return false;
-        }
-        status = "V";
-    } else {
-        status = table[0].data.c_str();
+        return false;
     }
 
+    auto status = table[0].data.c_str();
     if (*status == 'C' || *status == 'V') {
         return true;
     }
@@ -2168,7 +2142,7 @@
 }
 
 std::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+    if (!entry.fs_mgr_flags.avb) {
         return {};
     }
     DeviceMapper& dm = DeviceMapper::Instance();
@@ -2204,7 +2178,7 @@
 }
 
 bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+    if (!entry.fs_mgr_flags.avb) {
         return false;
     }
 
@@ -2340,3 +2314,22 @@
     LINFO << report << ret;
     return true;
 }
+
+bool fs_mgr_load_verity_state(int* mode) {
+    // unless otherwise specified, use EIO mode.
+    *mode = VERITY_MODE_EIO;
+
+    // The bootloader communicates verity mode via the kernel commandline
+    std::string verity_mode;
+    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+        return false;
+    }
+
+    if (verity_mode == "enforcing") {
+        *mode = VERITY_MODE_DEFAULT;
+    } else if (verity_mode == "logging") {
+        *mode = VERITY_MODE_LOGGING;
+    }
+
+    return true;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 07b533b..0ca1946 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -167,12 +167,10 @@
         CheckFlag("recoveryonly", recovery_only);
         CheckFlag("noemulatedsd", no_emulated_sd);
         CheckFlag("notrim", no_trim);
-        CheckFlag("verify", verify);
         CheckFlag("formattable", formattable);
         CheckFlag("slotselect", slot_select);
         CheckFlag("latemount", late_mount);
         CheckFlag("nofail", no_fail);
-        CheckFlag("verifyatboot", verify_at_boot);
         CheckFlag("quota", quota);
         CheckFlag("avb", avb);
         CheckFlag("logical", logical);
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
deleted file mode 100644
index efa2180..0000000
--- a/fs_mgr/fs_mgr_verity.cpp
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright (C) 2013 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 <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <crypto_utils/android_pubkey.h>
-#include <cutils/properties.h>
-#include <fs_mgr/file_wait.h>
-#include <libdm/dm.h>
-#include <logwrap/logwrap.h>
-#include <openssl/obj_mac.h>
-#include <openssl/rsa.h>
-#include <openssl/sha.h>
-
-#include "fec/io.h"
-
-#include "fs_mgr.h"
-#include "fs_mgr_dm_linear.h"
-#include "fs_mgr_priv.h"
-
-// Realistically, this file should be part of the android::fs_mgr namespace;
-using namespace android::fs_mgr;
-
-#define VERITY_TABLE_RSA_KEY "/verity_key"
-#define VERITY_TABLE_HASH_IDX 8
-#define VERITY_TABLE_SALT_IDX 9
-
-#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
-#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
-#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
-
-#define VERITY_TABLE_OPT_FEC_FORMAT \
-    "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
-    " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
-#define VERITY_TABLE_OPT_FEC_ARGS 9
-
-#define METADATA_MAGIC 0x01564c54
-#define METADATA_TAG_MAX_LENGTH 63
-#define METADATA_EOD "eod"
-
-#define VERITY_LASTSIG_TAG "verity_lastsig"
-
-#define VERITY_STATE_TAG "verity_state"
-#define VERITY_STATE_HEADER 0x83c0ae9d
-#define VERITY_STATE_VERSION 1
-
-#define VERITY_KMSG_RESTART "dm-verity device corrupted"
-#define VERITY_KMSG_BUFSIZE 1024
-
-#define READ_BUF_SIZE 4096
-
-#define __STRINGIFY(x) #x
-#define STRINGIFY(x) __STRINGIFY(x)
-
-struct verity_state {
-    uint32_t header;
-    uint32_t version;
-    int32_t mode;
-};
-
-extern struct fs_info info;
-
-static RSA *load_key(const char *path)
-{
-    uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
-
-    auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
-    if (!f) {
-        LERROR << "Can't open " << path;
-        return nullptr;
-    }
-
-    if (!fread(key_data, sizeof(key_data), 1, f.get())) {
-        LERROR << "Could not read key!";
-        return nullptr;
-    }
-
-    RSA* key = nullptr;
-    if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
-        LERROR << "Could not parse key!";
-        return nullptr;
-    }
-
-    return key;
-}
-
-static int verify_table(const uint8_t *signature, size_t signature_size,
-        const char *table, uint32_t table_length)
-{
-    RSA *key;
-    uint8_t hash_buf[SHA256_DIGEST_LENGTH];
-    int retval = -1;
-
-    // Hash the table
-    SHA256((uint8_t*)table, table_length, hash_buf);
-
-    // Now get the public key from the keyfile
-    key = load_key(VERITY_TABLE_RSA_KEY);
-    if (!key) {
-        LERROR << "Couldn't load verity keys";
-        goto out;
-    }
-
-    // verify the result
-    if (!RSA_verify(NID_sha256, hash_buf, sizeof(hash_buf), signature,
-                    signature_size, key)) {
-        LERROR << "Couldn't verify table";
-        goto out;
-    }
-
-    retval = 0;
-
-out:
-    RSA_free(key);
-    return retval;
-}
-
-static int verify_verity_signature(const struct fec_verity_metadata& verity)
-{
-    if (verify_table(verity.signature, sizeof(verity.signature),
-            verity.table, verity.table_length) == 0 ||
-        verify_table(verity.ecc_signature, sizeof(verity.ecc_signature),
-            verity.table, verity.table_length) == 0) {
-        return 0;
-    }
-
-    return -1;
-}
-
-static int invalidate_table(char *table, size_t table_length)
-{
-    size_t n = 0;
-    size_t idx = 0;
-    size_t cleared = 0;
-
-    while (n < table_length) {
-        if (table[n++] == ' ') {
-            ++idx;
-        }
-
-        if (idx != VERITY_TABLE_HASH_IDX && idx != VERITY_TABLE_SALT_IDX) {
-            continue;
-        }
-
-        while (n < table_length && table[n] != ' ') {
-            table[n++] = '0';
-        }
-
-        if (++cleared == 2) {
-            return 0;
-        }
-    }
-
-    return -1;
-}
-
-struct verity_table_params {
-    char *table;
-    int mode;
-    struct fec_ecc_metadata ecc;
-    const char *ecc_dev;
-};
-
-typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
-        const struct verity_table_params *params);
-
-static bool format_verity_table(char *buf, const size_t bufsize,
-        const struct verity_table_params *params)
-{
-    const char *mode_flag = NULL;
-    int res = -1;
-
-    if (params->mode == VERITY_MODE_RESTART) {
-        mode_flag = VERITY_TABLE_OPT_RESTART;
-    } else if (params->mode == VERITY_MODE_LOGGING) {
-        mode_flag = VERITY_TABLE_OPT_LOGGING;
-    }
-
-    if (params->ecc.valid) {
-        if (mode_flag) {
-            res = snprintf(buf, bufsize,
-                    "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
-                    params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
-                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
-        } else {
-            res = snprintf(buf, bufsize,
-                    "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
-                    params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
-                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
-        }
-    } else if (mode_flag) {
-        res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
-                    mode_flag);
-    } else {
-        res = snprintf(buf, bufsize, "%s 1 " VERITY_TABLE_OPT_IGNZERO, params->table);
-    }
-
-    if (res < 0 || (size_t)res >= bufsize) {
-        LERROR << "Error building verity table; insufficient buffer size?";
-        return false;
-    }
-
-    return true;
-}
-
-static bool format_legacy_verity_table(char *buf, const size_t bufsize,
-        const struct verity_table_params *params)
-{
-    int res;
-
-    if (params->mode == VERITY_MODE_EIO) {
-        res = strlcpy(buf, params->table, bufsize);
-    } else {
-        res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
-    }
-
-    if (res < 0 || (size_t)res >= bufsize) {
-        LERROR << "Error building verity table; insufficient buffer size?";
-        return false;
-    }
-
-    return true;
-}
-
-static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name,
-                             uint64_t device_size, const struct verity_table_params* params,
-                             format_verity_table_func format) {
-    android::dm::DmTable table;
-    table.set_readonly(true);
-
-    char buffer[DM_BUF_SIZE];
-    if (!format(buffer, sizeof(buffer), params)) {
-        LERROR << "Failed to format verity parameters";
-        return -1;
-    }
-
-    android::dm::DmTargetVerityString target(0, device_size / 512, buffer);
-    if (!table.AddTarget(std::make_unique<decltype(target)>(target))) {
-        LERROR << "Failed to add verity target";
-        return -1;
-    }
-    if (!dm.CreateDevice(name, table)) {
-        LERROR << "Failed to create verity device \"" << name << "\"";
-        return -1;
-    }
-    return 0;
-}
-
-static int read_partition(const char *path, uint64_t size)
-{
-    char buf[READ_BUF_SIZE];
-    ssize_t size_read;
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)));
-
-    if (fd == -1) {
-        PERROR << "Failed to open " << path;
-        return -errno;
-    }
-
-    while (size) {
-        size_read = TEMP_FAILURE_RETRY(read(fd, buf, READ_BUF_SIZE));
-        if (size_read == -1) {
-            PERROR << "Error in reading partition " << path;
-            return -errno;
-        }
-        size -= size_read;
-    }
-
-    return 0;
-}
-
-bool fs_mgr_load_verity_state(int* mode) {
-    // unless otherwise specified, use EIO mode.
-    *mode = VERITY_MODE_EIO;
-
-    // The bootloader communicates verity mode via the kernel commandline
-    std::string verity_mode;
-    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
-        return false;
-    }
-
-    if (verity_mode == "enforcing") {
-        *mode = VERITY_MODE_DEFAULT;
-    } else if (verity_mode == "logging") {
-        *mode = VERITY_MODE_LOGGING;
-    }
-
-    return true;
-}
-
-// Update the verity table using the actual block device path.
-// Two cases:
-// Case-1: verity table is shared for devices with different by-name prefix.
-// Example:
-//   verity table token:       /dev/block/bootdevice/by-name/vendor
-//   blk_device-1 (non-A/B):   /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
-//   blk_device-2 (A/B):       /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
-//
-// Case-2: append A/B suffix in the verity table.
-// Example:
-//   verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
-//   blk_device:         /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
-static void update_verity_table_blk_device(const std::string& blk_device, char** table,
-                                           bool slot_select) {
-    bool updated = false;
-    std::string result, ab_suffix;
-    auto tokens = android::base::Split(*table, " ");
-
-    // If slot_select is set, it means blk_device is already updated with ab_suffix.
-    if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
-
-    for (const auto& token : tokens) {
-        std::string new_token;
-        if (android::base::StartsWith(token, "/dev/block/")) {
-            if (token == blk_device) return;  // no need to update if they're already the same.
-            std::size_t found1 = blk_device.find("by-name");
-            std::size_t found2 = token.find("by-name");
-            if (found1 != std::string::npos && found2 != std::string::npos &&
-                blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
-                new_token = blk_device;
-            }
-        }
-
-        if (!new_token.empty()) {
-            updated = true;
-            LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
-                  << "'";
-        } else {
-            new_token = token;
-        }
-
-        if (result.empty()) {
-            result = new_token;
-        } else {
-            result += " " + new_token;
-        }
-    }
-
-    if (!updated) {
-        return;
-    }
-
-    free(*table);
-    *table = strdup(result.c_str());
-}
-
-// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
-// mount. The 'wait_for_verity_dev' parameter makes this function wait for the
-// verity device to get created before return
-int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) {
-    int retval = FS_MGR_SETUP_VERITY_FAIL;
-    int fd = -1;
-    std::string verity_blk_name;
-    struct fec_handle *f = NULL;
-    struct fec_verity_metadata verity;
-    struct verity_table_params params = { .table = NULL };
-
-    const std::string mount_point(basename(entry->mount_point.c_str()));
-    bool verified_at_boot = false;
-
-    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
-
-    if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) <
-        0) {
-        PERROR << "Failed to open '" << entry->blk_device << "'";
-        return retval;
-    }
-
-    // read verity metadata
-    if (fec_verity_get_metadata(f, &verity) < 0) {
-        PERROR << "Failed to get verity metadata '" << entry->blk_device << "'";
-        // Allow verity disabled when the device is unlocked without metadata
-        if (fs_mgr_is_device_unlocked()) {
-            retval = FS_MGR_SETUP_VERITY_SKIPPED;
-            LWARNING << "Allow invalid metadata when the device is unlocked";
-        }
-        goto out;
-    }
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
-    if (verity.disabled) {
-        retval = FS_MGR_SETUP_VERITY_DISABLED;
-        LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG";
-        goto out;
-    }
-#endif
-
-    // read ecc metadata
-    if (fec_ecc_get_metadata(f, &params.ecc) < 0) {
-        params.ecc.valid = false;
-    }
-
-    params.ecc_dev = entry->blk_device.c_str();
-
-    if (!fs_mgr_load_verity_state(&params.mode)) {
-        /* if accessing or updating the state failed, switch to the default
-         * safe mode. This makes sure the device won't end up in an endless
-         * restart loop, and no corrupted data will be exposed to userspace
-         * without a warning. */
-        params.mode = VERITY_MODE_EIO;
-    }
-
-    if (!verity.table) {
-        goto out;
-    }
-
-    params.table = strdup(verity.table);
-    if (!params.table) {
-        goto out;
-    }
-
-    // verify the signature on the table
-    if (verify_verity_signature(verity) < 0) {
-        // Allow signature verification error when the device is unlocked
-        if (fs_mgr_is_device_unlocked()) {
-            retval = FS_MGR_SETUP_VERITY_SKIPPED;
-            LWARNING << "Allow signature verification error when the device is unlocked";
-            goto out;
-        }
-        if (params.mode == VERITY_MODE_LOGGING) {
-            // the user has been warned, allow mounting without dm-verity
-            retval = FS_MGR_SETUP_VERITY_SKIPPED;
-            goto out;
-        }
-
-        // invalidate root hash and salt to trigger device-specific recovery
-        if (invalidate_table(params.table, verity.table_length) < 0) {
-            goto out;
-        }
-    }
-
-    LINFO << "Enabling dm-verity for " << mount_point.c_str()
-          << " (mode " << params.mode << ")";
-
-    // Update the verity params using the actual block device path
-    update_verity_table_blk_device(entry->blk_device, &params.table,
-                                   entry->fs_mgr_flags.slot_select);
-
-    // load the verity mapping table
-    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
-        goto loaded;
-    }
-
-    if (params.ecc.valid) {
-        // kernel may not support error correction, try without
-        LINFO << "Disabling error correction for " << mount_point.c_str();
-        params.ecc.valid = false;
-
-        if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
-            goto loaded;
-        }
-    }
-
-    // try the legacy format for backwards compatibility
-    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_legacy_verity_table) ==
-        0) {
-        goto loaded;
-    }
-
-    if (params.mode != VERITY_MODE_EIO) {
-        // as a last resort, EIO mode should always be supported
-        LINFO << "Falling back to EIO mode for " << mount_point.c_str();
-        params.mode = VERITY_MODE_EIO;
-
-        if (load_verity_table(dm, mount_point, verity.data_size, &params,
-                              format_legacy_verity_table) == 0) {
-            goto loaded;
-        }
-    }
-
-    LERROR << "Failed to load verity table for " << mount_point.c_str();
-    goto out;
-
-loaded:
-    if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) {
-        LERROR << "Couldn't get verity device number!";
-        goto out;
-    }
-
-    // mark the underlying block device as read-only
-    fs_mgr_set_blk_ro(entry->blk_device);
-
-    // Verify the entire partition in one go
-    // If there is an error, allow it to mount as a normal verity partition.
-    if (entry->fs_mgr_flags.verify_at_boot) {
-        LINFO << "Verifying partition " << entry->blk_device << " at boot";
-        int err = read_partition(verity_blk_name.c_str(), verity.data_size);
-        if (!err) {
-            LINFO << "Verified verity partition " << entry->blk_device << " at boot";
-            verified_at_boot = true;
-        }
-    }
-
-    // assign the new verity block device as the block device
-    if (!verified_at_boot) {
-        entry->blk_device = verity_blk_name;
-    } else if (!dm.DeleteDevice(mount_point)) {
-        LERROR << "Failed to remove verity device " << mount_point.c_str();
-        goto out;
-    }
-
-    // make sure we've set everything up properly
-    if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) {
-        goto out;
-    }
-
-    retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-
-    fec_close(f);
-    free(params.table);
-
-    return retval;
-}
-
-bool fs_mgr_teardown_verity(FstabEntry* entry) {
-    const std::string mount_point(basename(entry->mount_point.c_str()));
-    if (!android::fs_mgr::UnmapDevice(mount_point)) {
-        return false;
-    }
-    LINFO << "Unmapped verity device " << mount_point;
-    return true;
-}
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 054300e..b831d12 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -63,7 +63,6 @@
         bool nonremovable : 1;
         bool vold_managed : 1;
         bool recovery_only : 1;
-        bool verify : 1;
         bool no_emulated_sd : 1;  // No emulated sdcard daemon; sd card is the only external
                                   // storage.
         bool no_trim : 1;
@@ -72,7 +71,6 @@
         bool slot_select : 1;
         bool late_mount : 1;
         bool no_fail : 1;
-        bool verify_at_boot : 1;
         bool quota : 1;
         bool avb : 1;
         bool logical : 1;
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 332fcf5..1057d7f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 #include <stdint.h>
 #include <sys/sysmacros.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <chrono>
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6b0293a..8b269cd 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -236,52 +236,7 @@
         "libbrotli",
         "libc++fs",
         "libfs_mgr_binder",
-        "libgsi",
-        "libgmock",
-        "liblp",
-        "libsnapshot",
-        "libsnapshot_cow",
-        "libsnapshot_test_helpers",
-        "libsparse",
-    ],
-    header_libs: [
-        "libstorage_literals_headers",
-    ],
-    test_suites: [
-        "vts",
-        "device-tests"
-    ],
-    test_options: {
-        min_shipping_api_level: 29,
-    },
-    auto_gen_config: true,
-    require_root: true,
-}
-
-cc_defaults {
-    name: "userspace_snapshot_test_defaults",
-    defaults: ["libsnapshot_defaults"],
-    srcs: [
-        "partition_cow_creator_test.cpp",
-        "snapshot_metadata_updater_test.cpp",
-        "snapshot_reader_test.cpp",
-        "userspace_snapshot_test.cpp",
-        "snapshot_writer_test.cpp",
-    ],
-    shared_libs: [
-        "libbinder",
-        "libcrypto",
-        "libhidlbase",
-        "libprotobuf-cpp-lite",
-        "libutils",
-        "libz",
-    ],
-    static_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
-        "libbrotli",
-        "libc++fs",
-        "libfs_mgr_binder",
+        "libgflags",
         "libgsi",
         "libgmock",
         "liblp",
@@ -309,9 +264,15 @@
     defaults: ["libsnapshot_test_defaults"],
 }
 
-cc_test {
-    name: "vts_userspace_snapshot_test",
-    defaults: ["userspace_snapshot_test_defaults"],
+sh_test {
+    name: "run_snapshot_tests",
+    src: "run_snapshot_tests.sh",
+    test_suites: [
+        "device-tests",
+    ],
+    required: [
+        "vts_libsnapshot_test",
+    ],
 }
 
 cc_binary {
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 20030b9..9b5fd2a 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -475,10 +475,7 @@
         std::sort(other_ops.begin(), other_ops.end(), std::greater<int>());
     }
 
-    merge_op_blocks->reserve(merge_op_blocks->size() + other_ops.size());
-    for (auto block : other_ops) {
-        merge_op_blocks->emplace_back(block);
-    }
+    merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
 
     num_total_data_ops_ = merge_op_blocks->size();
     if (header_.num_merge_ops > 0) {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 08c3920..120f95b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -396,6 +396,17 @@
         DM_USER,
     };
 
+    // Add new public entries above this line.
+
+    // Helpers for failure injection.
+    using MergeConsistencyChecker =
+            std::function<MergeFailureCode(const std::string& name, const SnapshotStatus& status)>;
+
+    void set_merge_consistency_checker(MergeConsistencyChecker checker) {
+        merge_consistency_checker_ = checker;
+    }
+    MergeConsistencyChecker merge_consistency_checker() const { return merge_consistency_checker_; }
+
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -410,6 +421,7 @@
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
     FRIEND_TEST(SnapshotUpdateTest, AddPartition);
+    FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
     FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
@@ -793,7 +805,8 @@
 
     // Helper of UpdateUsesCompression
     bool UpdateUsesCompression(LockedFile* lock);
-    // Helper of UpdateUsesUsersnapshots
+    // Locked and unlocked functions to test whether the current update uses
+    // userspace snapshots.
     bool UpdateUsesUserSnapshots(LockedFile* lock);
 
     // Wrapper around libdm, with diagnostics.
@@ -810,6 +823,7 @@
     std::unique_ptr<SnapuserdClient> snapuserd_client_;
     std::unique_ptr<LpMetadata> old_partition_metadata_;
     std::optional<bool> is_snapshot_userspace_;
+    MergeConsistencyChecker merge_consistency_checker_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/run_snapshot_tests.sh b/fs_mgr/libsnapshot/run_snapshot_tests.sh
new file mode 100644
index 0000000..b03a4e0
--- /dev/null
+++ b/fs_mgr/libsnapshot/run_snapshot_tests.sh
@@ -0,0 +1,35 @@
+#!/system/bin/sh
+
+# Detect host or AOSP.
+getprop ro.build.version.sdk > /dev/null 2>&1
+if [ $? -eq 0 ]; then
+    cmd_prefix=""
+    local_root=""
+else
+    cmd_prefix="adb shell"
+    local_root="${ANDROID_PRODUCT_OUT}"
+    set -e
+    set -x
+    adb root
+    adb sync data
+    set +x
+    set +e
+fi
+
+testpath64="/data/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"
+testpath32="/data/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"
+if [ -f "${local_root}/${testpath64}" ]; then
+    testpath="${testpath64}"
+elif [ -f "${local_root}/${testpath32}" ]; then
+    testpath="${testpath32}"
+else
+    echo "ERROR: vts_libsnapshot_test not found." 1>&2
+    echo "Make sure to build vts_libsnapshot_test or snapshot_tests first." 1>&2
+    exit 1
+fi
+
+# Verbose, error on failure.
+set -x
+set -e
+
+time ${cmd_prefix} ${testpath}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index e6e17bd..18a9d22 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -87,6 +87,8 @@
 static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
 static constexpr auto kUpdateStateCheckInterval = 2s;
 
+MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status);
+
 // Note: IImageManager is an incomplete type in the header, so the default
 // destructor doesn't work.
 SnapshotManager::~SnapshotManager() {}
@@ -116,7 +118,9 @@
 }
 
 SnapshotManager::SnapshotManager(IDeviceInfo* device)
-    : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}
+    : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {
+    merge_consistency_checker_ = android::snapshot::CheckMergeConsistency;
+}
 
 static std::string GetCowName(const std::string& snapshot_name) {
     return snapshot_name + "-cow";
@@ -1329,14 +1333,20 @@
                                                         const SnapshotStatus& status) {
     CHECK(lock);
 
+    return merge_consistency_checker_(name, status);
+}
+
+MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
     if (!status.compression_enabled()) {
         // Do not try to verify old-style COWs yet.
         return MergeFailureCode::Ok;
     }
 
+    auto& dm = DeviceMapper::Instance();
+
     std::string cow_image_name = GetMappedCowDeviceName(name, status);
     std::string cow_image_path;
-    if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
+    if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
         LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
         return MergeFailureCode::GetCowPathConsistencyCheck;
     }
@@ -1400,9 +1410,11 @@
     }
 
     SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
-    CHECK(update_status.state() == UpdateState::Merging);
+    CHECK(update_status.state() == UpdateState::Merging ||
+          update_status.state() == UpdateState::MergeFailed);
     CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);
 
+    update_status.set_state(UpdateState::Merging);
     update_status.set_merge_phase(MergePhase::SECOND_PHASE);
     if (!WriteSnapshotUpdateStatus(lock, update_status)) {
         return MergeFailureCode::WriteStatus;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 7001b9a..11cebe1 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -34,6 +34,7 @@
 #include <fs_mgr/file_wait.h>
 #include <fs_mgr/roots.h>
 #include <fs_mgr_dm_linear.h>
+#include <gflags/gflags.h>
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
@@ -52,6 +53,8 @@
 #include <libsnapshot/mock_device_info.h>
 #include <libsnapshot/mock_snapshot.h>
 
+DEFINE_string(force_config, "", "Force testing mode (dmsnap, vab, vabc) ignoring device config.");
+
 namespace android {
 namespace snapshot {
 
@@ -87,6 +90,8 @@
 std::string fake_super;
 
 void MountMetadata();
+bool ShouldUseCompression();
+bool ShouldUseUserspaceSnapshots();
 
 class SnapshotTest : public ::testing::Test {
   public:
@@ -139,11 +144,7 @@
         std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
                                               "test_partition_b"};
         for (const auto& snapshot : snapshots) {
-            ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
-            DeleteBackingImage(image_manager_, snapshot + "-cow-img");
-
-            auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
-            android::base::RemoveFileIfExists(status_file);
+            CleanupSnapshotArtifacts(snapshot);
         }
 
         // Remove stale partitions in fake super.
@@ -151,7 +152,7 @@
                 "base-device",
                 "test_partition_b",
                 "test_partition_b-base",
-                "test_partition_b-base",
+                "test_partition_b-cow",
         };
         for (const auto& partition : partitions) {
             DeleteDevice(partition);
@@ -163,6 +164,32 @@
         }
     }
 
+    void CleanupSnapshotArtifacts(const std::string& snapshot) {
+        // The device-mapper stack may have been collapsed to dm-linear, so it's
+        // necessary to check what state it's in before attempting a cleanup.
+        // SnapshotManager has no path like this because we'd never remove a
+        // merged snapshot (a live partition).
+        bool is_dm_user = false;
+        DeviceMapper::TargetInfo target;
+        if (sm->IsSnapshotDevice(snapshot, &target)) {
+            is_dm_user = (DeviceMapper::GetTargetType(target.spec) == "user");
+        }
+
+        if (is_dm_user) {
+            ASSERT_TRUE(sm->EnsureSnapuserdConnected());
+            ASSERT_TRUE(AcquireLock());
+
+            auto local_lock = std::move(lock_);
+            ASSERT_TRUE(sm->UnmapUserspaceSnapshotDevice(local_lock.get(), snapshot));
+        }
+
+        ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
+        DeleteBackingImage(image_manager_, snapshot + "-cow-img");
+
+        auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
+        android::base::RemoveFileIfExists(status_file);
+    }
+
     bool AcquireLock() {
         lock_ = sm->LockExclusive();
         return !!lock_;
@@ -428,7 +455,7 @@
     ASSERT_TRUE(AcquireLock());
 
     PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = IsCompressionEnabled();
+    cow_creator.compression_enabled = ShouldUseCompression();
     if (cow_creator.compression_enabled) {
         cow_creator.compression_algorithm = "gz";
     } else {
@@ -469,7 +496,7 @@
     ASSERT_TRUE(AcquireLock());
 
     PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = IsCompressionEnabled();
+    cow_creator.compression_enabled = ShouldUseCompression();
 
     static const uint64_t kDeviceSize = 1024 * 1024;
     SnapshotStatus status;
@@ -527,6 +554,8 @@
     std::unique_ptr<ISnapshotWriter> writer;
     ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
 
+    bool userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
+
     // Release the lock.
     lock_ = nullptr;
 
@@ -548,7 +577,11 @@
     // The device should have been switched to a snapshot-merge target.
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
+    if (userspace_snapshots) {
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
+    } else {
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
+    }
 
     // We should not be able to cancel an update now.
     ASSERT_FALSE(sm->CancelUpdate());
@@ -584,11 +617,13 @@
 
     ASSERT_TRUE(AcquireLock());
 
+    bool userspace_snapshots = init->UpdateUsesUserSnapshots(lock_.get());
+
     // Validate that we have a snapshot device.
     SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
     ASSERT_EQ(status.state(), SnapshotState::CREATED);
-    if (IsCompressionEnabled()) {
+    if (ShouldUseCompression()) {
         ASSERT_EQ(status.compression_algorithm(), "gz");
     } else {
         ASSERT_EQ(status.compression_algorithm(), "none");
@@ -596,7 +631,11 @@
 
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+    if (userspace_snapshots) {
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
+    } else {
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+    }
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
@@ -858,7 +897,7 @@
         opener_ = std::make_unique<TestPartitionOpener>(fake_super);
 
         auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
-        dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
+        dynamic_partition_metadata->set_vabc_enabled(ShouldUseCompression());
         dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
 
         // Create a fake update package metadata.
@@ -991,7 +1030,7 @@
     }
 
     AssertionResult MapOneUpdateSnapshot(const std::string& name) {
-        if (IsCompressionEnabled()) {
+        if (ShouldUseCompression()) {
             std::unique_ptr<ISnapshotWriter> writer;
             return MapUpdateSnapshot(name, &writer);
         } else {
@@ -1001,7 +1040,7 @@
     }
 
     AssertionResult WriteSnapshotAndHash(const std::string& name) {
-        if (IsCompressionEnabled()) {
+        if (ShouldUseCompression()) {
             std::unique_ptr<ISnapshotWriter> writer;
             auto res = MapUpdateSnapshot(name, &writer);
             if (!res) {
@@ -1169,7 +1208,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
+    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
     {
         // We should have started in SECOND_PHASE since nothing shrinks.
         ASSERT_TRUE(AcquireLock());
@@ -1196,7 +1235,7 @@
 }
 
 TEST_F(SnapshotUpdateTest, DuplicateOps) {
-    if (!IsCompressionEnabled()) {
+    if (!ShouldUseCompression()) {
         GTEST_SKIP() << "Compression-only test";
     }
 
@@ -1240,7 +1279,7 @@
 // Test that shrinking and growing partitions at the same time is handled
 // correctly in VABC.
 TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
-    if (!IsCompressionEnabled()) {
+    if (!ShouldUseCompression()) {
         // b/179111359
         GTEST_SKIP() << "Skipping Virtual A/B Compression test";
     }
@@ -1303,7 +1342,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
+    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
     {
         // Check that the merge phase is FIRST_PHASE until at least one call
         // to ProcessUpdateState() occurs.
@@ -1320,11 +1359,21 @@
     // Check that we used the correct types after rebooting mid-merge.
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(init->IsSnapshotDevice("prd_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
-    ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
-    ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+
+    bool userspace_snapshots = init->UpdateUsesUserSnapshots();
+    if (userspace_snapshots) {
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
+        ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
+        ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
+    } else {
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
+        ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+        ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
+        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+    }
 
     // Complete the merge.
     ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
@@ -1345,6 +1394,93 @@
     }
 }
 
+// Test that a transient merge consistency check failure can resume properly.
+TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
+    if (!ShouldUseCompression()) {
+        // b/179111359
+        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+    }
+
+    auto old_sys_size = GetSize(sys_);
+    auto old_prd_size = GetSize(prd_);
+
+    // Grow |sys| but shrink |prd|.
+    SetSize(sys_, old_sys_size * 2);
+    sys_->set_estimate_cow_size(8_MiB);
+    SetSize(prd_, old_prd_size / 2);
+    prd_->set_estimate_cow_size(1_MiB);
+
+    AddOperationForPartitions();
+
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+    ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
+
+    sync();
+
+    // Assert that source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = NewManagerForFirstStageMount("_b");
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+    // Check that the target partitions have the same content.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
+    auto old_checker = init->merge_consistency_checker();
+
+    init->set_merge_consistency_checker(
+            [](const std::string&, const SnapshotStatus&) -> MergeFailureCode {
+                return MergeFailureCode::WrongMergeCountConsistencyCheck;
+            });
+
+    // Initiate the merge and wait for it to be completed.
+    ASSERT_TRUE(init->InitiateMerge());
+    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
+    {
+        // Check that the merge phase is FIRST_PHASE until at least one call
+        // to ProcessUpdateState() occurs.
+        ASSERT_TRUE(AcquireLock());
+        auto local_lock = std::move(lock_);
+        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
+        ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
+    }
+
+    // Merge should have failed.
+    ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
+
+    // Simulate shutting down the device and creating partitions again.
+    ASSERT_TRUE(UnmapAll());
+
+    // Restore the checker.
+    init->set_merge_consistency_checker(std::move(old_checker));
+
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+    // Complete the merge.
+    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+    // Check that the target partitions have the same content after the merge.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name))
+                << "Content of " << name << " changes after the merge";
+    }
+}
+
 // Test that if new system partitions uses empty space in super, that region is not snapshotted.
 TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
     GTEST_SKIP() << "b/141889746";
@@ -1802,6 +1938,8 @@
 
     ASSERT_TRUE(new_sm->FinishMergeInRecovery());
 
+    ASSERT_TRUE(UnmapAll());
+
     auto mount = new_sm->EnsureMetadataMounted();
     ASSERT_TRUE(mount && mount->HasDevice());
     ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
@@ -1894,6 +2032,8 @@
     ASSERT_FALSE(test_device->IsSlotUnbootable(1));
     ASSERT_FALSE(test_device->IsSlotUnbootable(0));
 
+    ASSERT_TRUE(UnmapAll());
+
     // Now reboot into new slot.
     test_device = new TestDeviceInfo(fake_super, "_b");
     auto init = NewManagerForFirstStageMount(test_device);
@@ -1922,8 +2062,8 @@
         ASSERT_TRUE(AcquireLock());
 
         PartitionCowCreator cow_creator = {
-                .compression_enabled = IsCompressionEnabled(),
-                .compression_algorithm = IsCompressionEnabled() ? "gz" : "none",
+                .compression_enabled = ShouldUseCompression(),
+                .compression_algorithm = ShouldUseCompression() ? "gz" : "none",
         };
         SnapshotStatus status;
         status.set_name("sys_a");
@@ -1955,6 +2095,8 @@
     ASSERT_FALSE(test_device->IsSlotUnbootable(1));
     ASSERT_FALSE(test_device->IsSlotUnbootable(0));
 
+    ASSERT_TRUE(UnmapAll());
+
     // Now reboot into new slot.
     test_device = new TestDeviceInfo(fake_super, "_b");
     auto init = NewManagerForFirstStageMount(test_device);
@@ -2017,7 +2159,7 @@
 
 // Test for overflow bit after update
 TEST_F(SnapshotUpdateTest, Overflow) {
-    if (IsCompressionEnabled()) {
+    if (ShouldUseCompression()) {
         GTEST_SKIP() << "No overflow bit set for userspace COWs";
     }
 
@@ -2152,7 +2294,7 @@
 };
 
 TEST_F(SnapshotUpdateTest, DaemonTransition) {
-    if (!IsCompressionEnabled()) {
+    if (!ShouldUseCompression()) {
         GTEST_SKIP() << "Skipping Virtual A/B Compression test";
     }
 
@@ -2178,21 +2320,38 @@
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
-    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
-    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
+    bool userspace_snapshots = init->UpdateUsesUserSnapshots();
+
+    if (userspace_snapshots) {
+        ASSERT_EQ(access("/dev/dm-user/sys_b-init", F_OK), 0);
+        ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), -1);
+    } else {
+        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
+        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
+    }
 
     ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
 
     // :TODO: this is a workaround to ensure the handler list stays empty. We
     // should make this test more like actual init, and spawn two copies of
     // snapuserd, given how many other tests we now have for normal snapuserd.
-    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-user-cow-init"));
-    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-user-cow-init"));
-    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-user-cow-init"));
+    if (userspace_snapshots) {
+        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-init"));
+        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-init"));
+        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-init"));
 
-    // The control device should have been renamed.
-    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
-    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
+        // The control device should have been renamed.
+        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-init", 10s));
+        ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), 0);
+    } else {
+        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-user-cow-init"));
+        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-user-cow-init"));
+        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-user-cow-init"));
+
+        // The control device should have been renamed.
+        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
+        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
+    }
 }
 
 TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
@@ -2233,8 +2392,13 @@
             },
             &path));
 
-    // Hold sys_a open so it can't be unmapped.
-    unique_fd fd(open(path.c_str(), O_RDONLY));
+    bool userspace_snapshots = sm->UpdateUsesUserSnapshots();
+
+    unique_fd fd;
+    if (!userspace_snapshots) {
+        // Hold sys_a open so it can't be unmapped.
+        fd.reset(open(path.c_str(), O_RDONLY));
+    }
 
     // Switch back to "A", make sure we can cancel. Instead of unmapping sys_a
     // we should simply delete the old snapshots.
@@ -2253,6 +2417,11 @@
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    if (sm->UpdateUsesUserSnapshots()) {
+        GTEST_SKIP() << "Test does not apply to userspace snapshots";
+    }
+
     ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
     ASSERT_TRUE(UnmapAll());
@@ -2557,21 +2726,53 @@
     }
 }
 
+bool ShouldUseUserspaceSnapshots() {
+    if (FLAGS_force_config == "dmsnap") {
+        return false;
+    }
+    if (!FLAGS_force_config.empty()) {
+        return true;
+    }
+    return IsUserspaceSnapshotsEnabled();
+}
+
+bool ShouldUseCompression() {
+    if (FLAGS_force_config == "vab" || FLAGS_force_config == "dmsnap") {
+        return false;
+    }
+    if (FLAGS_force_config == "vabc") {
+        return true;
+    }
+    return IsCompressionEnabled();
+}
+
 }  // namespace snapshot
 }  // namespace android
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
+    gflags::ParseCommandLineFlags(&argc, &argv, false);
 
     android::base::SetProperty("ctl.stop", "snapuserd");
 
-    if (!android::base::SetProperty("snapuserd.test.dm.snapshots", "1")) {
-        return testing::AssertionFailure()
-               << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+    std::unordered_set<std::string> configs = {"", "dmsnap", "vab", "vabc"};
+    if (configs.count(FLAGS_force_config) == 0) {
+        std::cerr << "Unexpected force_config argument\n";
+        return 1;
+    }
+
+    if (FLAGS_force_config == "dmsnap") {
+        if (!android::base::SetProperty("snapuserd.test.dm.snapshots", "1")) {
+            return testing::AssertionFailure()
+                   << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+        }
     }
 
     int ret = RUN_ALL_TESTS();
-    android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
+
+    if (FLAGS_force_config == "dmsnap") {
+        android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
+    }
     return ret;
 }
diff --git a/fs_mgr/libsnapshot/userspace_snapshot_test.cpp b/fs_mgr/libsnapshot/userspace_snapshot_test.cpp
deleted file mode 100644
index abe67f6..0000000
--- a/fs_mgr/libsnapshot/userspace_snapshot_test.cpp
+++ /dev/null
@@ -1,2519 +0,0 @@
-// Copyright (C) 2018 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 <libsnapshot/cow_format.h>
-#include <libsnapshot/snapshot.h>
-
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <chrono>
-#include <deque>
-#include <future>
-#include <iostream>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr/file_wait.h>
-#include <fs_mgr/roots.h>
-#include <fs_mgr_dm_linear.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/builder.h>
-#include <storage_literals/storage_literals.h>
-
-#include <android/snapshot/snapshot.pb.h>
-#include <libsnapshot/test_helpers.h>
-#include "partition_cow_creator.h"
-#include "utility.h"
-
-#include <android-base/properties.h>
-
-// Mock classes are not used. Header included to ensure mocked class definition aligns with the
-// class itself.
-#include <libsnapshot/mock_device_info.h>
-#include <libsnapshot/mock_snapshot.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::dm::IDeviceMapper;
-using android::fiemap::FiemapStatus;
-using android::fiemap::IImageManager;
-using android::fs_mgr::BlockDeviceInfo;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fs_mgr::DestroyLogicalPartition;
-using android::fs_mgr::EnsurePathMounted;
-using android::fs_mgr::EnsurePathUnmounted;
-using android::fs_mgr::Extent;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::GetPartitionGroupName;
-using android::fs_mgr::GetPartitionName;
-using android::fs_mgr::Interval;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::SlotSuffixForSlotNumber;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::DynamicPartitionGroup;
-using chromeos_update_engine::PartitionUpdate;
-using namespace ::testing;
-using namespace android::storage_literals;
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-
-// Global states. See test_helpers.h.
-std::unique_ptr<SnapshotManager> sm;
-TestDeviceInfo* test_device = nullptr;
-std::string fake_super;
-
-void MountMetadata();
-
-class SnapshotTest : public ::testing::Test {
-  public:
-    SnapshotTest() : dm_(DeviceMapper::Instance()) {}
-
-    // This is exposed for main.
-    void Cleanup() {
-        InitializeState();
-        CleanupTestArtifacts();
-    }
-
-  protected:
-    void SetUp() override {
-        SKIP_IF_NON_VIRTUAL_AB();
-
-        SnapshotTestPropertyFetcher::SetUp();
-        InitializeState();
-        CleanupTestArtifacts();
-        FormatFakeSuper();
-        MountMetadata();
-        ASSERT_TRUE(sm->BeginUpdate());
-    }
-
-    void TearDown() override {
-        RETURN_IF_NON_VIRTUAL_AB();
-
-        lock_ = nullptr;
-
-        CleanupTestArtifacts();
-        SnapshotTestPropertyFetcher::TearDown();
-    }
-
-    void InitializeState() {
-        ASSERT_TRUE(sm->EnsureImageManager());
-        image_manager_ = sm->image_manager();
-
-        test_device->set_slot_suffix("_a");
-
-        sm->set_use_first_stage_snapuserd(false);
-    }
-
-    void CleanupTestArtifacts() {
-        // Normally cancelling inside a merge is not allowed. Since these
-        // are tests, we don't care, destroy everything that might exist.
-        // Note we hardcode this list because of an annoying quirk: when
-        // completing a merge, the snapshot stops existing, so we can't
-        // get an accurate list to remove.
-        lock_ = nullptr;
-
-        std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
-                                              "test_partition_b"};
-        for (const auto& snapshot : snapshots) {
-            ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
-            DeleteBackingImage(image_manager_, snapshot + "-cow-img");
-
-            auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
-            android::base::RemoveFileIfExists(status_file);
-        }
-
-        // Remove stale partitions in fake super.
-        std::vector<std::string> partitions = {
-                "base-device",
-                "test_partition_b",
-                "test_partition_b-base",
-                "test_partition_b-base",
-        };
-        for (const auto& partition : partitions) {
-            DeleteDevice(partition);
-        }
-
-        if (sm->GetUpdateState() != UpdateState::None) {
-            auto state_file = sm->GetStateFilePath();
-            unlink(state_file.c_str());
-        }
-    }
-
-    bool AcquireLock() {
-        lock_ = sm->LockExclusive();
-        return !!lock_;
-    }
-
-    // This is so main() can instantiate this to invoke Cleanup.
-    virtual void TestBody() override {}
-
-    void FormatFakeSuper() {
-        BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4096);
-        std::vector<BlockDeviceInfo> devices = {super_device};
-
-        auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
-        ASSERT_NE(builder, nullptr);
-
-        auto metadata = builder->Export();
-        ASSERT_NE(metadata, nullptr);
-
-        TestPartitionOpener opener(fake_super);
-        ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *metadata.get()));
-    }
-
-    // If |path| is non-null, the partition will be mapped after creation.
-    bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr,
-                         const std::optional<std::string> group = {}) {
-        TestPartitionOpener opener(fake_super);
-        auto builder = MetadataBuilder::New(opener, "super", 0);
-        if (!builder) return false;
-
-        std::string partition_group = std::string(android::fs_mgr::kDefaultGroup);
-        if (group) {
-            partition_group = *group;
-        }
-        return CreatePartition(builder.get(), name, size, path, partition_group);
-    }
-
-    bool CreatePartition(MetadataBuilder* builder, const std::string& name, uint64_t size,
-                         std::string* path, const std::string& group) {
-        auto partition = builder->AddPartition(name, group, 0);
-        if (!partition) return false;
-        if (!builder->ResizePartition(partition, size)) {
-            return false;
-        }
-
-        // Update the source slot.
-        auto metadata = builder->Export();
-        if (!metadata) return false;
-
-        TestPartitionOpener opener(fake_super);
-        if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
-            return false;
-        }
-
-        if (!path) return true;
-
-        CreateLogicalPartitionParams params = {
-                .block_device = fake_super,
-                .metadata = metadata.get(),
-                .partition_name = name,
-                .force_writable = true,
-                .timeout_ms = 10s,
-        };
-        return CreateLogicalPartition(params, path);
-    }
-
-    AssertionResult MapUpdateSnapshot(const std::string& name,
-                                      std::unique_ptr<ISnapshotWriter>* writer) {
-        TestPartitionOpener opener(fake_super);
-        CreateLogicalPartitionParams params{
-                .block_device = fake_super,
-                .metadata_slot = 1,
-                .partition_name = name,
-                .timeout_ms = 10s,
-                .partition_opener = &opener,
-        };
-
-        auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
-        auto result = sm->OpenSnapshotWriter(params, {old_partition});
-        if (!result) {
-            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
-        }
-        if (!result->Initialize()) {
-            return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
-        }
-
-        if (writer) {
-            *writer = std::move(result);
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
-        TestPartitionOpener opener(fake_super);
-        CreateLogicalPartitionParams params{
-                .block_device = fake_super,
-                .metadata_slot = 1,
-                .partition_name = name,
-                .timeout_ms = 10s,
-                .partition_opener = &opener,
-        };
-
-        auto result = sm->MapUpdateSnapshot(params, path);
-        if (!result) {
-            return AssertionFailure() << "Cannot open snapshot for writing: " << name;
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {
-        AssertionResult res = AssertionSuccess();
-        if (!(res = DeleteDevice(snapshot))) return res;
-        if (!sm->UnmapDmUserDevice(snapshot + "-user-cow")) {
-            return AssertionFailure() << "Cannot delete dm-user device for " << snapshot;
-        }
-        if (!(res = DeleteDevice(snapshot + "-inner"))) return res;
-        if (!(res = DeleteDevice(snapshot + "-cow"))) return res;
-        if (!image_manager_->UnmapImageIfExists(snapshot + "-cow-img")) {
-            return AssertionFailure() << "Cannot unmap image " << snapshot << "-cow-img";
-        }
-        if (!(res = DeleteDevice(snapshot + "-base"))) return res;
-        if (!(res = DeleteDevice(snapshot + "-src"))) return res;
-        return AssertionSuccess();
-    }
-
-    AssertionResult DeleteDevice(const std::string& device) {
-        if (!dm_.DeleteDeviceIfExists(device)) {
-            return AssertionFailure() << "Can't delete " << device;
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult CreateCowImage(const std::string& name) {
-        if (!sm->CreateCowImage(lock_.get(), name)) {
-            return AssertionFailure() << "Cannot create COW image " << name;
-        }
-        std::string cow_device;
-        auto map_res = MapCowImage(name, 10s, &cow_device);
-        if (!map_res) {
-            return map_res;
-        }
-        if (!InitializeKernelCow(cow_device)) {
-            return AssertionFailure() << "Cannot zero fill " << cow_device;
-        }
-        if (!sm->UnmapCowImage(name)) {
-            return AssertionFailure() << "Cannot unmap " << name << " after zero filling it";
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult MapCowImage(const std::string& name,
-                                const std::chrono::milliseconds& timeout_ms, std::string* path) {
-        auto cow_image_path = sm->MapCowImage(name, timeout_ms);
-        if (!cow_image_path.has_value()) {
-            return AssertionFailure() << "Cannot map cow image " << name;
-        }
-        *path = *cow_image_path;
-        return AssertionSuccess();
-    }
-
-    // Prepare A/B slot for a partition named "test_partition".
-    AssertionResult PrepareOneSnapshot(uint64_t device_size,
-                                       std::unique_ptr<ISnapshotWriter>* writer = nullptr) {
-        lock_ = nullptr;
-
-        DeltaArchiveManifest manifest;
-
-        auto dynamic_partition_metadata = manifest.mutable_dynamic_partition_metadata();
-        dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
-        dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
-
-        auto group = dynamic_partition_metadata->add_groups();
-        group->set_name("group");
-        group->set_size(device_size * 2);
-        group->add_partition_names("test_partition");
-
-        auto pu = manifest.add_partitions();
-        pu->set_partition_name("test_partition");
-        pu->set_estimate_cow_size(device_size);
-        SetSize(pu, device_size);
-
-        auto extent = pu->add_operations()->add_dst_extents();
-        extent->set_start_block(0);
-        if (device_size) {
-            extent->set_num_blocks(device_size / manifest.block_size());
-        }
-
-        TestPartitionOpener opener(fake_super);
-        auto builder = MetadataBuilder::New(opener, "super", 0);
-        if (!builder) {
-            return AssertionFailure() << "Failed to open MetadataBuilder";
-        }
-        builder->AddGroup("group_a", 16_GiB);
-        builder->AddGroup("group_b", 16_GiB);
-        if (!CreatePartition(builder.get(), "test_partition_a", device_size, nullptr, "group_a")) {
-            return AssertionFailure() << "Failed create test_partition_a";
-        }
-
-        if (!sm->CreateUpdateSnapshots(manifest)) {
-            return AssertionFailure() << "Failed to create update snapshots";
-        }
-
-        if (writer) {
-            auto res = MapUpdateSnapshot("test_partition_b", writer);
-            if (!res) {
-                return res;
-            }
-        } else if (!IsCompressionEnabled()) {
-            std::string ignore;
-            if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
-                return AssertionFailure() << "Failed to map test_partition_b";
-            }
-        }
-        if (!AcquireLock()) {
-            return AssertionFailure() << "Failed to acquire lock";
-        }
-        return AssertionSuccess();
-    }
-
-    // Simulate a reboot into the new slot.
-    AssertionResult SimulateReboot() {
-        lock_ = nullptr;
-        if (!sm->FinishedSnapshotWrites(false)) {
-            return AssertionFailure() << "Failed to finish snapshot writes";
-        }
-        if (!sm->UnmapUpdateSnapshot("test_partition_b")) {
-            return AssertionFailure() << "Failed to unmap COW for test_partition_b";
-        }
-        if (!dm_.DeleteDeviceIfExists("test_partition_b")) {
-            return AssertionFailure() << "Failed to delete test_partition_b";
-        }
-        if (!dm_.DeleteDeviceIfExists("test_partition_b-base")) {
-            return AssertionFailure() << "Failed to destroy test_partition_b-base";
-        }
-        return AssertionSuccess();
-    }
-
-    std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(
-            const std::string& slot_suffix = "_a") {
-        auto info = new TestDeviceInfo(fake_super, slot_suffix);
-        return NewManagerForFirstStageMount(info);
-    }
-
-    std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(TestDeviceInfo* info) {
-        info->set_first_stage_init(true);
-        auto init = SnapshotManager::NewForFirstStageMount(info);
-        if (!init) {
-            return nullptr;
-        }
-        init->SetUeventRegenCallback([](const std::string& device) -> bool {
-            return android::fs_mgr::WaitForFile(device, snapshot_timeout_);
-        });
-        return init;
-    }
-
-    static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
-    DeviceMapper& dm_;
-    std::unique_ptr<SnapshotManager::LockedFile> lock_;
-    android::fiemap::IImageManager* image_manager_ = nullptr;
-    std::string fake_super_;
-};
-
-TEST_F(SnapshotTest, CreateSnapshot) {
-    ASSERT_TRUE(AcquireLock());
-
-    PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = IsCompressionEnabled();
-    if (cow_creator.compression_enabled) {
-        cow_creator.compression_algorithm = "gz";
-    } else {
-        cow_creator.compression_algorithm = "none";
-    }
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::vector<std::string> snapshots;
-    ASSERT_TRUE(sm->ListSnapshots(lock_.get(), &snapshots));
-    ASSERT_EQ(snapshots.size(), 1);
-    ASSERT_EQ(snapshots[0], "test-snapshot");
-
-    // Scope so delete can re-acquire the snapshot file lock.
-    {
-        SnapshotStatus status;
-        ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), "test-snapshot", &status));
-        ASSERT_EQ(status.state(), SnapshotState::CREATED);
-        ASSERT_EQ(status.device_size(), kDeviceSize);
-        ASSERT_EQ(status.snapshot_size(), kDeviceSize);
-        ASSERT_EQ(status.compression_enabled(), cow_creator.compression_enabled);
-        ASSERT_EQ(status.compression_algorithm(), cow_creator.compression_algorithm);
-    }
-
-    ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
-    ASSERT_TRUE(sm->UnmapCowImage("test-snapshot"));
-    ASSERT_TRUE(sm->DeleteSnapshot(lock_.get(), "test-snapshot"));
-}
-
-TEST_F(SnapshotTest, MapSnapshot) {
-    ASSERT_TRUE(AcquireLock());
-
-    PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = IsCompressionEnabled();
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::string base_device;
-    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-
-    std::string cow_device;
-    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
-
-    std::string snap_device;
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
-                                &snap_device));
-    ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
-}
-
-TEST_F(SnapshotTest, NoMergeBeforeReboot) {
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Merge should fail, since the slot hasn't changed.
-    ASSERT_FALSE(sm->InitiateMerge());
-}
-
-TEST_F(SnapshotTest, CleanFirstStageMount) {
-    // If there's no update in progress, there should be no first-stage mount
-    // needed.
-    auto sm = NewManagerForFirstStageMount();
-    ASSERT_NE(sm, nullptr);
-    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
-}
-
-TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // We didn't change the slot, so we shouldn't need snapshots.
-    auto sm = NewManagerForFirstStageMount();
-    ASSERT_NE(sm, nullptr);
-    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
-
-    auto indicator = sm->GetRollbackIndicatorPath();
-    ASSERT_EQ(access(indicator.c_str(), R_OK), 0);
-}
-
-TEST_F(SnapshotTest, Merge) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-
-    std::unique_ptr<ISnapshotWriter> writer;
-    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
-
-    // Release the lock.
-    lock_ = nullptr;
-
-    std::string test_string = "This is a test string.";
-    test_string.resize(writer->options().block_size);
-    ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
-    ASSERT_TRUE(writer->Finalize());
-    writer = nullptr;
-
-    // Done updating.
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    ASSERT_TRUE(sm->UnmapUpdateSnapshot("test_partition_b"));
-
-    test_device->set_slot_suffix("_b");
-    ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    ASSERT_TRUE(sm->InitiateMerge());
-
-    // The device should have been switched to a snapshot-merge target.
-    DeviceMapper::TargetInfo target;
-    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-
-    // We should not be able to cancel an update now.
-    ASSERT_FALSE(sm->CancelUpdate());
-
-    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
-    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
-
-    // The device should no longer be a snapshot or snapshot-merge.
-    ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
-
-    // Test that we can read back the string we wrote to the snapshot. Note
-    // that the base device is gone now. |snap_device| contains the correct
-    // partition.
-    unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
-    ASSERT_GE(fd, 0);
-
-    std::string buffer(test_string.size(), '\0');
-    ASSERT_TRUE(android::base::ReadFully(fd, buffer.data(), buffer.size()));
-    ASSERT_EQ(test_string, buffer);
-}
-
-TEST_F(SnapshotTest, FirstStageMountAndMerge) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
-    ASSERT_TRUE(SimulateReboot());
-
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    ASSERT_TRUE(AcquireLock());
-
-    // Validate that we have a snapshot device.
-    SnapshotStatus status;
-    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
-    ASSERT_EQ(status.state(), SnapshotState::CREATED);
-    if (IsCompressionEnabled()) {
-        ASSERT_EQ(status.compression_algorithm(), "gz");
-    } else {
-        ASSERT_EQ(status.compression_algorithm(), "none");
-    }
-
-    DeviceMapper::TargetInfo target;
-    ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-}
-
-TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
-    ASSERT_TRUE(SimulateReboot());
-
-    // Reflash the super partition.
-    FormatFakeSuper();
-    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
-
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    ASSERT_TRUE(AcquireLock());
-
-    SnapshotStatus status;
-    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
-
-    // We should not get a snapshot device now.
-    DeviceMapper::TargetInfo target;
-    ASSERT_FALSE(init->IsSnapshotDevice("test_partition_b", &target));
-
-    // We should see a cancelled update as well.
-    lock_ = nullptr;
-    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
-}
-
-TEST_F(SnapshotTest, FlashSuperDuringMerge) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
-    ASSERT_TRUE(SimulateReboot());
-
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    ASSERT_TRUE(init->InitiateMerge());
-
-    // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
-    // status is still Merging.
-    ASSERT_TRUE(DeleteSnapshotDevice("test_partition_b"));
-    ASSERT_TRUE(init->image_manager()->UnmapImageIfExists("test_partition_b-cow-img"));
-    FormatFakeSuper();
-    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Because the status is Merging, we must call ProcessUpdateState, which should
-    // detect a cancelled update.
-    ASSERT_EQ(init->ProcessUpdateState(), UpdateState::Cancelled);
-    ASSERT_EQ(init->GetUpdateState(), UpdateState::None);
-}
-
-TEST_F(SnapshotTest, UpdateBootControlHal) {
-    ASSERT_TRUE(AcquireLock());
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Initiated));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Merging));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeNeedsReboot));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeCompleted));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeFailed));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
-}
-
-TEST_F(SnapshotTest, MergeFailureCode) {
-    ASSERT_TRUE(AcquireLock());
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeFailed,
-                                     MergeFailureCode::ListSnapshots));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
-
-    SnapshotUpdateStatus status = sm->ReadSnapshotUpdateStatus(lock_.get());
-    ASSERT_EQ(status.state(), UpdateState::MergeFailed);
-    ASSERT_EQ(status.merge_failure_code(), MergeFailureCode::ListSnapshots);
-}
-
-enum class Request { UNKNOWN, LOCK_SHARED, LOCK_EXCLUSIVE, UNLOCK, EXIT };
-std::ostream& operator<<(std::ostream& os, Request request) {
-    switch (request) {
-        case Request::LOCK_SHARED:
-            return os << "Shared";
-        case Request::LOCK_EXCLUSIVE:
-            return os << "Exclusive";
-        case Request::UNLOCK:
-            return os << "Unlock";
-        case Request::EXIT:
-            return os << "Exit";
-        case Request::UNKNOWN:
-            [[fallthrough]];
-        default:
-            return os << "Unknown";
-    }
-}
-
-class LockTestConsumer {
-  public:
-    AssertionResult MakeRequest(Request new_request) {
-        {
-            std::unique_lock<std::mutex> ulock(mutex_);
-            requests_.push_back(new_request);
-        }
-        cv_.notify_all();
-        return AssertionSuccess() << "Request " << new_request << " successful";
-    }
-
-    template <typename R, typename P>
-    AssertionResult WaitFulfill(std::chrono::duration<R, P> timeout) {
-        std::unique_lock<std::mutex> ulock(mutex_);
-        if (cv_.wait_for(ulock, timeout, [this] { return requests_.empty(); })) {
-            return AssertionSuccess() << "All requests_ fulfilled.";
-        }
-        return AssertionFailure() << "Timeout waiting for fulfilling " << requests_.size()
-                                  << " request(s), first one is "
-                                  << (requests_.empty() ? Request::UNKNOWN : requests_.front());
-    }
-
-    void StartHandleRequestsInBackground() {
-        future_ = std::async(std::launch::async, &LockTestConsumer::HandleRequests, this);
-    }
-
-  private:
-    void HandleRequests() {
-        static constexpr auto consumer_timeout = 3s;
-
-        auto next_request = Request::UNKNOWN;
-        do {
-            // Peek next request.
-            {
-                std::unique_lock<std::mutex> ulock(mutex_);
-                if (cv_.wait_for(ulock, consumer_timeout, [this] { return !requests_.empty(); })) {
-                    next_request = requests_.front();
-                } else {
-                    next_request = Request::EXIT;
-                }
-            }
-
-            // Handle next request.
-            switch (next_request) {
-                case Request::LOCK_SHARED: {
-                    lock_ = sm->LockShared();
-                } break;
-                case Request::LOCK_EXCLUSIVE: {
-                    lock_ = sm->LockExclusive();
-                } break;
-                case Request::EXIT:
-                    [[fallthrough]];
-                case Request::UNLOCK: {
-                    lock_.reset();
-                } break;
-                case Request::UNKNOWN:
-                    [[fallthrough]];
-                default:
-                    break;
-            }
-
-            // Pop next request. This thread is the only thread that
-            // pops from the front of the requests_ deque.
-            {
-                std::unique_lock<std::mutex> ulock(mutex_);
-                if (next_request == Request::EXIT) {
-                    requests_.clear();
-                } else {
-                    requests_.pop_front();
-                }
-            }
-            cv_.notify_all();
-        } while (next_request != Request::EXIT);
-    }
-
-    std::mutex mutex_;
-    std::condition_variable cv_;
-    std::deque<Request> requests_;
-    std::unique_ptr<SnapshotManager::LockedFile> lock_;
-    std::future<void> future_;
-};
-
-class LockTest : public ::testing::Test {
-  public:
-    void SetUp() {
-        SKIP_IF_NON_VIRTUAL_AB();
-        first_consumer.StartHandleRequestsInBackground();
-        second_consumer.StartHandleRequestsInBackground();
-    }
-
-    void TearDown() {
-        RETURN_IF_NON_VIRTUAL_AB();
-        EXPECT_TRUE(first_consumer.MakeRequest(Request::EXIT));
-        EXPECT_TRUE(second_consumer.MakeRequest(Request::EXIT));
-    }
-
-    static constexpr auto request_timeout = 500ms;
-    LockTestConsumer first_consumer;
-    LockTestConsumer second_consumer;
-};
-
-TEST_F(LockTest, SharedShared) {
-    ASSERT_TRUE(first_consumer.MakeRequest(Request::LOCK_SHARED));
-    ASSERT_TRUE(first_consumer.WaitFulfill(request_timeout));
-    ASSERT_TRUE(second_consumer.MakeRequest(Request::LOCK_SHARED));
-    ASSERT_TRUE(second_consumer.WaitFulfill(request_timeout));
-}
-
-using LockTestParam = std::pair<Request, Request>;
-class LockTestP : public LockTest, public ::testing::WithParamInterface<LockTestParam> {};
-TEST_P(LockTestP, Test) {
-    ASSERT_TRUE(first_consumer.MakeRequest(GetParam().first));
-    ASSERT_TRUE(first_consumer.WaitFulfill(request_timeout));
-    ASSERT_TRUE(second_consumer.MakeRequest(GetParam().second));
-    ASSERT_FALSE(second_consumer.WaitFulfill(request_timeout))
-            << "Should not be able to " << GetParam().second << " while separate thread "
-            << GetParam().first;
-    ASSERT_TRUE(first_consumer.MakeRequest(Request::UNLOCK));
-    ASSERT_TRUE(second_consumer.WaitFulfill(request_timeout))
-            << "Should be able to hold lock that is released by separate thread";
-}
-INSTANTIATE_TEST_SUITE_P(
-        LockTest, LockTestP,
-        testing::Values(LockTestParam{Request::LOCK_EXCLUSIVE, Request::LOCK_EXCLUSIVE},
-                        LockTestParam{Request::LOCK_EXCLUSIVE, Request::LOCK_SHARED},
-                        LockTestParam{Request::LOCK_SHARED, Request::LOCK_EXCLUSIVE}),
-        [](const testing::TestParamInfo<LockTestP::ParamType>& info) {
-            std::stringstream ss;
-            ss << info.param.first << info.param.second;
-            return ss.str();
-        });
-
-class SnapshotUpdateTest : public SnapshotTest {
-  public:
-    void SetUp() override {
-        SKIP_IF_NON_VIRTUAL_AB();
-
-        SnapshotTest::SetUp();
-        Cleanup();
-
-        // Cleanup() changes slot suffix, so initialize it again.
-        test_device->set_slot_suffix("_a");
-
-        opener_ = std::make_unique<TestPartitionOpener>(fake_super);
-
-        auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
-        dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
-        dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
-
-        // Create a fake update package metadata.
-        // Not using full name "system", "vendor", "product" because these names collide with the
-        // mapped partitions on the running device.
-        // Each test modifies manifest_ slightly to indicate changes to the partition layout.
-        group_ = dynamic_partition_metadata->add_groups();
-        group_->set_name("group");
-        group_->set_size(kGroupSize);
-        group_->add_partition_names("sys");
-        group_->add_partition_names("vnd");
-        group_->add_partition_names("prd");
-        sys_ = manifest_.add_partitions();
-        sys_->set_partition_name("sys");
-        sys_->set_estimate_cow_size(2_MiB);
-        SetSize(sys_, 3_MiB);
-        vnd_ = manifest_.add_partitions();
-        vnd_->set_partition_name("vnd");
-        vnd_->set_estimate_cow_size(2_MiB);
-        SetSize(vnd_, 3_MiB);
-        prd_ = manifest_.add_partitions();
-        prd_->set_partition_name("prd");
-        prd_->set_estimate_cow_size(2_MiB);
-        SetSize(prd_, 3_MiB);
-
-        // Initialize source partition metadata using |manifest_|.
-        src_ = MetadataBuilder::New(*opener_, "super", 0);
-        ASSERT_NE(src_, nullptr);
-        ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
-        // Add sys_b which is like system_other.
-        ASSERT_TRUE(src_->AddGroup("group_b", kGroupSize));
-        auto partition = src_->AddPartition("sys_b", "group_b", 0);
-        ASSERT_NE(nullptr, partition);
-        ASSERT_TRUE(src_->ResizePartition(partition, 1_MiB));
-        auto metadata = src_->Export();
-        ASSERT_NE(nullptr, metadata);
-        ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
-
-        // Map source partitions. Additionally, map sys_b to simulate system_other after flashing.
-        std::string path;
-        for (const auto& name : {"sys_a", "vnd_a", "prd_a", "sys_b"}) {
-            ASSERT_TRUE(CreateLogicalPartition(
-                    CreateLogicalPartitionParams{
-                            .block_device = fake_super,
-                            .metadata_slot = 0,
-                            .partition_name = name,
-                            .timeout_ms = 1s,
-                            .partition_opener = opener_.get(),
-                    },
-                    &path));
-            ASSERT_TRUE(WriteRandomData(path));
-            auto hash = GetHash(path);
-            ASSERT_TRUE(hash.has_value());
-            hashes_[name] = *hash;
-        }
-
-        // OTA client blindly unmaps all partitions that are possibly mapped.
-        for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-            ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-        }
-    }
-    void TearDown() override {
-        RETURN_IF_NON_VIRTUAL_AB();
-
-        Cleanup();
-        SnapshotTest::TearDown();
-    }
-    void Cleanup() {
-        if (!image_manager_) {
-            InitializeState();
-        }
-        MountMetadata();
-        for (const auto& suffix : {"_a", "_b"}) {
-            test_device->set_slot_suffix(suffix);
-
-            // Cheat our way out of merge failed states.
-            if (sm->ProcessUpdateState() == UpdateState::MergeFailed) {
-                ASSERT_TRUE(AcquireLock());
-                ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));
-                lock_ = {};
-            }
-
-            EXPECT_TRUE(sm->CancelUpdate()) << suffix;
-        }
-        EXPECT_TRUE(UnmapAll());
-    }
-
-    AssertionResult IsPartitionUnchanged(const std::string& name) {
-        std::string path;
-        if (!dm_.GetDmDevicePathByName(name, &path)) {
-            return AssertionFailure() << "Path of " << name << " cannot be determined";
-        }
-        auto hash = GetHash(path);
-        if (!hash.has_value()) {
-            return AssertionFailure() << "Cannot read partition " << name << ": " << path;
-        }
-        auto it = hashes_.find(name);
-        if (it == hashes_.end()) {
-            return AssertionFailure() << "No existing hash for " << name << ". Bad test code?";
-        }
-        if (it->second != *hash) {
-            return AssertionFailure() << "Content of " << name << " has changed";
-        }
-        return AssertionSuccess();
-    }
-
-    std::optional<uint64_t> GetSnapshotSize(const std::string& name) {
-        if (!AcquireLock()) {
-            return std::nullopt;
-        }
-        auto local_lock = std::move(lock_);
-
-        SnapshotStatus status;
-        if (!sm->ReadSnapshotStatus(local_lock.get(), name, &status)) {
-            return std::nullopt;
-        }
-        return status.snapshot_size();
-    }
-
-    AssertionResult UnmapAll() {
-        for (const auto& name : {"sys", "vnd", "prd", "dlkm"}) {
-            if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
-                return AssertionFailure() << "Cannot unmap " << name << "_a";
-            }
-            if (!DeleteSnapshotDevice(name + "_b"s)) {
-                return AssertionFailure() << "Cannot delete snapshot " << name << "_b";
-            }
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult MapOneUpdateSnapshot(const std::string& name) {
-        if (IsCompressionEnabled()) {
-            std::unique_ptr<ISnapshotWriter> writer;
-            return MapUpdateSnapshot(name, &writer);
-        } else {
-            std::string path;
-            return MapUpdateSnapshot(name, &path);
-        }
-    }
-
-    AssertionResult WriteSnapshotAndHash(const std::string& name) {
-        if (IsCompressionEnabled()) {
-            std::unique_ptr<ISnapshotWriter> writer;
-            auto res = MapUpdateSnapshot(name, &writer);
-            if (!res) {
-                return res;
-            }
-            if (!WriteRandomData(writer.get(), &hashes_[name])) {
-                return AssertionFailure() << "Unable to write random data to snapshot " << name;
-            }
-            if (!writer->Finalize()) {
-                return AssertionFailure() << "Unable to finalize COW for " << name;
-            }
-        } else {
-            std::string path;
-            auto res = MapUpdateSnapshot(name, &path);
-            if (!res) {
-                return res;
-            }
-            if (!WriteRandomData(path, std::nullopt, &hashes_[name])) {
-                return AssertionFailure() << "Unable to write random data to snapshot " << name;
-            }
-        }
-
-        // Make sure updates to one device are seen by all devices.
-        sync();
-
-        return AssertionSuccess() << "Written random data to snapshot " << name
-                                  << ", hash: " << hashes_[name];
-    }
-
-    // Generate a snapshot that moves all the upper blocks down to the start.
-    // It doesn't really matter the order, we just want copies that reference
-    // blocks that won't exist if the partition shrinks.
-    AssertionResult ShiftAllSnapshotBlocks(const std::string& name, uint64_t old_size) {
-        std::unique_ptr<ISnapshotWriter> writer;
-        if (auto res = MapUpdateSnapshot(name, &writer); !res) {
-            return res;
-        }
-        if (!writer->options().max_blocks || !*writer->options().max_blocks) {
-            return AssertionFailure() << "No max blocks set for " << name << " writer";
-        }
-
-        uint64_t src_block = (old_size / writer->options().block_size) - 1;
-        uint64_t dst_block = 0;
-        uint64_t max_blocks = *writer->options().max_blocks;
-        while (dst_block < max_blocks && dst_block < src_block) {
-            if (!writer->AddCopy(dst_block, src_block)) {
-                return AssertionFailure() << "Unable to add copy for " << name << " for blocks "
-                                          << src_block << ", " << dst_block;
-            }
-            dst_block++;
-            src_block--;
-        }
-        if (!writer->Finalize()) {
-            return AssertionFailure() << "Unable to finalize writer for " << name;
-        }
-
-        auto hash = HashSnapshot(writer.get());
-        if (hash.empty()) {
-            return AssertionFailure() << "Unable to hash snapshot writer for " << name;
-        }
-        hashes_[name] = hash;
-
-        return AssertionSuccess();
-    }
-
-    AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b",
-                                                                                "prd_b"}) {
-        for (const auto& name : names) {
-            auto res = MapOneUpdateSnapshot(name);
-            if (!res) {
-                return res;
-            }
-        }
-        return AssertionSuccess();
-    }
-
-    // Create fake install operations to grow the COW device size.
-    void AddOperation(PartitionUpdate* partition_update, uint64_t size_bytes = 0) {
-        auto e = partition_update->add_operations()->add_dst_extents();
-        e->set_start_block(0);
-        if (size_bytes == 0) {
-            size_bytes = GetSize(partition_update);
-        }
-        e->set_num_blocks(size_bytes / manifest_.block_size());
-    }
-
-    void AddOperationForPartitions(std::vector<PartitionUpdate*> partitions = {}) {
-        if (partitions.empty()) {
-            partitions = {sys_, vnd_, prd_};
-        }
-        for (auto* partition : partitions) {
-            AddOperation(partition);
-        }
-    }
-
-    std::unique_ptr<TestPartitionOpener> opener_;
-    DeltaArchiveManifest manifest_;
-    std::unique_ptr<MetadataBuilder> src_;
-    std::map<std::string, std::string> hashes_;
-
-    PartitionUpdate* sys_ = nullptr;
-    PartitionUpdate* vnd_ = nullptr;
-    PartitionUpdate* prd_ = nullptr;
-    DynamicPartitionGroup* group_ = nullptr;
-};
-
-// Test full update flow executed by update_engine. Some partitions uses super empty space,
-// some uses images, and some uses both.
-// Also test UnmapUpdateSnapshot unmaps everything.
-// Also test first stage mount and merge after this.
-TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
-    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
-    // fit in super, but not |prd|.
-    constexpr uint64_t partition_size = 3788_KiB;
-    SetSize(sys_, partition_size);
-    SetSize(vnd_, partition_size);
-    SetSize(prd_, 18_MiB);
-
-    // Make sure |prd| does not fit in super at all. On VABC, this means we
-    // fake an extra large COW for |vnd| to fill up super.
-    vnd_->set_estimate_cow_size(30_MiB);
-    prd_->set_estimate_cow_size(30_MiB);
-
-    AddOperationForPartitions();
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Test that partitions prioritize using space in super.
-    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(tgt, nullptr);
-    ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
-    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
-    ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    auto indicator = sm->GetRollbackIndicatorPath();
-    ASSERT_NE(access(indicator.c_str(), R_OK), 0);
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
-    {
-        // We should have started in SECOND_PHASE since nothing shrinks.
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
-        ASSERT_EQ(status.merge_phase(), MergePhase::SECOND_PHASE);
-    }
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-
-    // Make sure the second phase ran and deleted snapshots.
-    {
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        std::vector<std::string> snapshots;
-        ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));
-        ASSERT_TRUE(snapshots.empty());
-    }
-
-    // Check that the target partitions have the same content after the merge.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name))
-                << "Content of " << name << " changes after the merge";
-    }
-}
-
-TEST_F(SnapshotUpdateTest, DuplicateOps) {
-    if (!IsCompressionEnabled()) {
-        GTEST_SKIP() << "Compression-only test";
-    }
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
-    for (auto* partition : partitions) {
-        AddOperation(partition);
-
-        std::unique_ptr<ISnapshotWriter> writer;
-        auto res = MapUpdateSnapshot(partition->partition_name() + "_b", &writer);
-        ASSERT_TRUE(res);
-        ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
-        ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
-        ASSERT_TRUE(writer->Finalize());
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-}
-
-// Test that shrinking and growing partitions at the same time is handled
-// correctly in VABC.
-TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
-    if (!IsCompressionEnabled()) {
-        // b/179111359
-        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
-    }
-
-    auto old_sys_size = GetSize(sys_);
-    auto old_prd_size = GetSize(prd_);
-
-    // Grow |sys| but shrink |prd|.
-    SetSize(sys_, old_sys_size * 2);
-    sys_->set_estimate_cow_size(8_MiB);
-    SetSize(prd_, old_prd_size / 2);
-    prd_->set_estimate_cow_size(1_MiB);
-
-    AddOperationForPartitions();
-
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Check that the old partition sizes were saved correctly.
-    {
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-
-        SnapshotStatus status;
-        ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), "prd_b", &status));
-        ASSERT_EQ(status.old_partition_size(), 3145728);
-        ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), "sys_b", &status));
-        ASSERT_EQ(status.old_partition_size(), 3145728);
-    }
-
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-    ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
-    ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
-
-    sync();
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    auto indicator = sm->GetRollbackIndicatorPath();
-    ASSERT_NE(access(indicator.c_str(), R_OK), 0);
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
-    {
-        // Check that the merge phase is FIRST_PHASE until at least one call
-        // to ProcessUpdateState() occurs.
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
-        ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
-    }
-
-    // Simulate shutting down the device and creating partitions again.
-    ASSERT_TRUE(UnmapAll());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Check that we used the correct types after rebooting mid-merge.
-    DeviceMapper::TargetInfo target;
-    ASSERT_TRUE(init->IsSnapshotDevice("prd_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-    ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-    ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-
-    // Complete the merge.
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-
-    // Make sure the second phase ran and deleted snapshots.
-    {
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        std::vector<std::string> snapshots;
-        ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));
-        ASSERT_TRUE(snapshots.empty());
-    }
-
-    // Check that the target partitions have the same content after the merge.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name))
-                << "Content of " << name << " changes after the merge";
-    }
-}
-
-// Test that if new system partitions uses empty space in super, that region is not snapshotted.
-TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
-    GTEST_SKIP() << "b/141889746";
-    SetSize(sys_, 4_MiB);
-    // vnd_b and prd_b are unchanged.
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_EQ(3_MiB, GetSnapshotSize("sys_b").value_or(0));
-}
-
-// Test that if new system partitions uses space of old vendor partition, that region is
-// snapshotted.
-TEST_F(SnapshotUpdateTest, SnapshotOldPartitions) {
-    SetSize(sys_, 4_MiB);  // grows
-    SetSize(vnd_, 2_MiB);  // shrinks
-    // prd_b is unchanged
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_EQ(4_MiB, GetSnapshotSize("sys_b").value_or(0));
-}
-
-// Test that even if there seem to be empty space in target metadata, COW partition won't take
-// it because they are used by old partitions.
-TEST_F(SnapshotUpdateTest, CowPartitionDoNotTakeOldPartitions) {
-    SetSize(sys_, 2_MiB);  // shrinks
-    // vnd_b and prd_b are unchanged.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(nullptr, tgt);
-    auto metadata = tgt->Export();
-    ASSERT_NE(nullptr, metadata);
-    std::vector<std::string> written;
-    // Write random data to all COW partitions in super
-    for (auto p : metadata->partitions) {
-        if (GetPartitionGroupName(metadata->groups[p.group_index]) != kCowGroupName) {
-            continue;
-        }
-        std::string path;
-        ASSERT_TRUE(CreateLogicalPartition(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata = metadata.get(),
-                        .partition = &p,
-                        .timeout_ms = 1s,
-                        .partition_opener = opener_.get(),
-                },
-                &path));
-        ASSERT_TRUE(WriteRandomData(path));
-        written.push_back(GetPartitionName(p));
-    }
-    ASSERT_FALSE(written.empty())
-            << "No COW partitions are created even if there are empty space in super partition";
-
-    // Make sure source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-}
-
-// Test that it crashes after creating snapshot status file but before creating COW image, then
-// calling CreateUpdateSnapshots again works.
-TEST_F(SnapshotUpdateTest, SnapshotStatusFileWithoutCow) {
-    // Write some trash snapshot files to simulate leftovers from previous runs.
-    {
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        SnapshotStatus status;
-        status.set_name("sys_b");
-        ASSERT_TRUE(sm->WriteSnapshotStatus(local_lock.get(), status));
-        ASSERT_TRUE(image_manager_->CreateBackingImage("sys_b-cow-img", 1_MiB,
-                                                       IImageManager::CREATE_IMAGE_DEFAULT));
-    }
-
-    // Redo the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
-
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Check that target partitions can be mapped.
-    EXPECT_TRUE(MapUpdateSnapshots());
-}
-
-// Test that the old partitions are not modified.
-TEST_F(SnapshotUpdateTest, TestRollback) {
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
-
-    AddOperationForPartitions();
-
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    // Simulate shutting down the device again.
-    ASSERT_TRUE(UnmapAll());
-    init = NewManagerForFirstStageMount("_a");
-    ASSERT_NE(init, nullptr);
-    ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Assert that the source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-}
-
-// Test that if an update is applied but not booted into, it can be canceled.
-TEST_F(SnapshotUpdateTest, CancelAfterApply) {
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-    ASSERT_TRUE(sm->CancelUpdate());
-}
-
-static std::vector<Interval> ToIntervals(const std::vector<std::unique_ptr<Extent>>& extents) {
-    std::vector<Interval> ret;
-    std::transform(extents.begin(), extents.end(), std::back_inserter(ret),
-                   [](const auto& extent) { return extent->AsLinearExtent()->AsInterval(); });
-    return ret;
-}
-
-// Test that at the second update, old COW partition spaces are reclaimed.
-TEST_F(SnapshotUpdateTest, ReclaimCow) {
-    // Make sure VABC cows are small enough that they fit in fake_super.
-    sys_->set_estimate_cow_size(64_KiB);
-    vnd_->set_estimate_cow_size(64_KiB);
-    prd_->set_estimate_cow_size(64_KiB);
-
-    // Execute the first update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    init = nullptr;
-
-    // Initiate the merge and wait for it to be completed.
-    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_TRUE(new_sm->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
-
-    // Execute the second update.
-    ASSERT_TRUE(new_sm->BeginUpdate());
-    ASSERT_TRUE(new_sm->CreateUpdateSnapshots(manifest_));
-
-    // Check that the old COW space is reclaimed and does not occupy space of mapped partitions.
-    auto src = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(src, nullptr);
-    auto tgt = MetadataBuilder::New(*opener_, "super", 0);
-    ASSERT_NE(tgt, nullptr);
-    for (const auto& cow_part_name : {"sys_a-cow", "vnd_a-cow", "prd_a-cow"}) {
-        auto* cow_part = tgt->FindPartition(cow_part_name);
-        ASSERT_NE(nullptr, cow_part) << cow_part_name << " does not exist in target metadata";
-        auto cow_intervals = ToIntervals(cow_part->extents());
-        for (const auto& old_part_name : {"sys_b", "vnd_b", "prd_b"}) {
-            auto* old_part = src->FindPartition(old_part_name);
-            ASSERT_NE(nullptr, old_part) << old_part_name << " does not exist in source metadata";
-            auto old_intervals = ToIntervals(old_part->extents());
-
-            auto intersect = Interval::Intersect(cow_intervals, old_intervals);
-            ASSERT_TRUE(intersect.empty()) << "COW uses space of source partitions";
-        }
-    }
-}
-
-TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
-    constexpr auto kRetrofitGroupSize = kGroupSize / 2;
-
-    // Initialize device-mapper / disk
-    ASSERT_TRUE(UnmapAll());
-    FormatFakeSuper();
-
-    // Setup source partition metadata to have both _a and _b partitions.
-    src_ = MetadataBuilder::New(*opener_, "super", 0);
-    ASSERT_NE(nullptr, src_);
-    for (const auto& suffix : {"_a"s, "_b"s}) {
-        ASSERT_TRUE(src_->AddGroup(group_->name() + suffix, kRetrofitGroupSize));
-        for (const auto& name : {"sys"s, "vnd"s, "prd"s}) {
-            auto partition = src_->AddPartition(name + suffix, group_->name() + suffix, 0);
-            ASSERT_NE(nullptr, partition);
-            ASSERT_TRUE(src_->ResizePartition(partition, 2_MiB));
-        }
-    }
-    auto metadata = src_->Export();
-    ASSERT_NE(nullptr, metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
-
-    // Flash source partitions
-    std::string path;
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(CreateLogicalPartition(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 0,
-                        .partition_name = name,
-                        .timeout_ms = 1s,
-                        .partition_opener = opener_.get(),
-                },
-                &path));
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
-    }
-
-    // Setup manifest.
-    group_->set_size(kRetrofitGroupSize);
-    for (auto* partition : {sys_, vnd_, prd_}) {
-        SetSize(partition, 2_MiB);
-    }
-    AddOperationForPartitions();
-
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Test that COW image should not be created for retrofit devices; super
-    // should be big enough.
-    ASSERT_FALSE(image_manager_->BackingImageExists("sys_b-cow-img"));
-    ASSERT_FALSE(image_manager_->BackingImageExists("vnd_b-cow-img"));
-    ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-}
-
-TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
-    // Make source partitions as big as possible to force COW image to be created.
-    SetSize(sys_, 10_MiB);
-    SetSize(vnd_, 10_MiB);
-    SetSize(prd_, 10_MiB);
-    sys_->set_estimate_cow_size(12_MiB);
-    vnd_->set_estimate_cow_size(12_MiB);
-    prd_->set_estimate_cow_size(12_MiB);
-
-    src_ = MetadataBuilder::New(*opener_, "super", 0);
-    ASSERT_NE(src_, nullptr);
-    src_->RemoveGroupAndPartitions(group_->name() + "_a");
-    src_->RemoveGroupAndPartitions(group_->name() + "_b");
-    ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
-    auto metadata = src_->Export();
-    ASSERT_NE(nullptr, metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
-
-    // Add operations for sys. The whole device is written.
-    AddOperation(sys_);
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    // Normally we should use NewManagerForFirstStageMount, but if so,
-    // "gsid.mapped_image.sys_b-cow-img" won't be set.
-    auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Keep an open handle to the cow device. This should cause the merge to
-    // be incomplete.
-    auto cow_path = android::base::GetProperty("gsid.mapped_image.sys_b-cow-img", "");
-    unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
-    ASSERT_GE(fd, 0);
-
-    // COW cannot be removed due to open fd, so expect a soft failure.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState());
-
-    // Simulate shutting down the device.
-    fd.reset();
-    ASSERT_TRUE(UnmapAll());
-
-    // init does first stage mount again.
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // sys_b should be mapped as a dm-linear device directly.
-    ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
-
-    // Merge should be able to complete now.
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-}
-
-class MetadataMountedTest : public ::testing::Test {
-  public:
-    // This is so main() can instantiate this to invoke Cleanup.
-    virtual void TestBody() override {}
-    void SetUp() override {
-        SKIP_IF_NON_VIRTUAL_AB();
-        metadata_dir_ = test_device->GetMetadataDir();
-        ASSERT_TRUE(ReadDefaultFstab(&fstab_));
-    }
-    void TearDown() override {
-        RETURN_IF_NON_VIRTUAL_AB();
-        SetUp();
-        // Remount /metadata
-        test_device->set_recovery(false);
-        EXPECT_TRUE(android::fs_mgr::EnsurePathMounted(&fstab_, metadata_dir_));
-    }
-    AssertionResult IsMetadataMounted() {
-        Fstab mounted_fstab;
-        if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
-            ADD_FAILURE() << "Failed to scan mounted volumes";
-            return AssertionFailure() << "Failed to scan mounted volumes";
-        }
-
-        auto entry = GetEntryForPath(&fstab_, metadata_dir_);
-        if (entry == nullptr) {
-            return AssertionFailure() << "No mount point found in fstab for path " << metadata_dir_;
-        }
-
-        auto mv = GetEntryForMountPoint(&mounted_fstab, entry->mount_point);
-        if (mv == nullptr) {
-            return AssertionFailure() << metadata_dir_ << " is not mounted";
-        }
-        return AssertionSuccess() << metadata_dir_ << " is mounted";
-    }
-    std::string metadata_dir_;
-    Fstab fstab_;
-};
-
-void MountMetadata() {
-    MetadataMountedTest().TearDown();
-}
-
-TEST_F(MetadataMountedTest, Android) {
-    auto device = sm->EnsureMetadataMounted();
-    EXPECT_NE(nullptr, device);
-    device.reset();
-
-    EXPECT_TRUE(IsMetadataMounted());
-    EXPECT_TRUE(sm->CancelUpdate()) << "Metadata dir should never be unmounted in Android mode";
-}
-
-TEST_F(MetadataMountedTest, Recovery) {
-    test_device->set_recovery(true);
-    metadata_dir_ = test_device->GetMetadataDir();
-
-    EXPECT_TRUE(android::fs_mgr::EnsurePathUnmounted(&fstab_, metadata_dir_));
-    EXPECT_FALSE(IsMetadataMounted());
-
-    auto device = sm->EnsureMetadataMounted();
-    EXPECT_NE(nullptr, device);
-    EXPECT_TRUE(IsMetadataMounted());
-
-    device.reset();
-    EXPECT_FALSE(IsMetadataMounted());
-}
-
-// Test that during a merge, we can wipe data in recovery.
-TEST_F(SnapshotUpdateTest, MergeInRecovery) {
-    // Execute the first update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    init = nullptr;
-
-    // Initiate the merge and then immediately stop it to simulate a reboot.
-    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_TRUE(new_sm->InitiateMerge());
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate a reboot into recovery.
-    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
-    test_device->set_recovery(true);
-    new_sm = NewManagerForFirstStageMount(test_device.release());
-
-    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
-    ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
-}
-
-// Test that a merge does not clear the snapshot state in fastboot.
-TEST_F(SnapshotUpdateTest, MergeInFastboot) {
-    // Execute the first update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    init = nullptr;
-
-    // Initiate the merge and then immediately stop it to simulate a reboot.
-    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_TRUE(new_sm->InitiateMerge());
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate a reboot into recovery.
-    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
-    test_device->set_recovery(true);
-    new_sm = NewManagerForFirstStageMount(test_device.release());
-
-    ASSERT_TRUE(new_sm->FinishMergeInRecovery());
-
-    ASSERT_TRUE(UnmapAll());
-
-    auto mount = new_sm->EnsureMetadataMounted();
-    ASSERT_TRUE(mount && mount->HasDevice());
-    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
-
-    // Finish the merge in a normal boot.
-    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
-    init = NewManagerForFirstStageMount(test_device.release());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    init = nullptr;
-
-    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
-    new_sm = NewManagerForFirstStageMount(test_device.release());
-    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
-    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
-}
-
-// Test that after an OTA, before a merge, we can wipe data in recovery.
-TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
-    // Execute the first update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate a reboot into recovery.
-    auto test_device = new TestDeviceInfo(fake_super, "_b");
-    test_device->set_recovery(true);
-    auto new_sm = NewManagerForFirstStageMount(test_device);
-
-    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
-    // Manually mount metadata so that we can call GetUpdateState() below.
-    MountMetadata();
-    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
-    EXPECT_TRUE(test_device->IsSlotUnbootable(1));
-    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
-}
-
-// Test that after an OTA and a bootloader rollback with no merge, we can wipe
-// data in recovery.
-TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
-    // Execute the first update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate a rollback, with reboot into recovery.
-    auto test_device = new TestDeviceInfo(fake_super, "_a");
-    test_device->set_recovery(true);
-    auto new_sm = NewManagerForFirstStageMount(test_device);
-
-    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
-    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
-    EXPECT_FALSE(test_device->IsSlotUnbootable(0));
-    EXPECT_FALSE(test_device->IsSlotUnbootable(1));
-}
-
-// Test update package that requests data wipe.
-TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
-    AddOperationForPartitions();
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate a reboot into recovery.
-    auto test_device = new TestDeviceInfo(fake_super, "_b");
-    test_device->set_recovery(true);
-    auto new_sm = NewManagerForFirstStageMount(test_device);
-
-    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
-    // Manually mount metadata so that we can call GetUpdateState() below.
-    MountMetadata();
-    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
-    ASSERT_FALSE(test_device->IsSlotUnbootable(1));
-    ASSERT_FALSE(test_device->IsSlotUnbootable(0));
-
-    ASSERT_TRUE(UnmapAll());
-
-    // Now reboot into new slot.
-    test_device = new TestDeviceInfo(fake_super, "_b");
-    auto init = NewManagerForFirstStageMount(test_device);
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    // Verify that we are on the downgraded build.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name)) << name;
-    }
-}
-
-// Test update package that requests data wipe.
-TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
-    AddOperationForPartitions();
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
-    }
-
-    // Create a stale snapshot that should not exist.
-    {
-        ASSERT_TRUE(AcquireLock());
-
-        PartitionCowCreator cow_creator = {
-                .compression_enabled = IsCompressionEnabled(),
-                .compression_algorithm = IsCompressionEnabled() ? "gz" : "none",
-        };
-        SnapshotStatus status;
-        status.set_name("sys_a");
-        status.set_device_size(1_MiB);
-        status.set_snapshot_size(2_MiB);
-        status.set_cow_partition_size(2_MiB);
-
-        ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));
-        lock_ = nullptr;
-
-        ASSERT_TRUE(sm->EnsureImageManager());
-        ASSERT_TRUE(sm->image_manager()->CreateBackingImage("sys_a", 1_MiB, 0));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate a reboot into recovery.
-    auto test_device = new TestDeviceInfo(fake_super, "_b");
-    test_device->set_recovery(true);
-    auto new_sm = NewManagerForFirstStageMount(test_device);
-
-    ASSERT_TRUE(new_sm->HandleImminentDataWipe());
-    // Manually mount metadata so that we can call GetUpdateState() below.
-    MountMetadata();
-    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
-    ASSERT_FALSE(test_device->IsSlotUnbootable(1));
-    ASSERT_FALSE(test_device->IsSlotUnbootable(0));
-
-    ASSERT_TRUE(UnmapAll());
-
-    // Now reboot into new slot.
-    test_device = new TestDeviceInfo(fake_super, "_b");
-    auto init = NewManagerForFirstStageMount(test_device);
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    // Verify that we are on the downgraded build.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name)) << name;
-    }
-}
-
-TEST_F(SnapshotUpdateTest, Hashtree) {
-    constexpr auto partition_size = 4_MiB;
-    constexpr auto data_size = 3_MiB;
-    constexpr auto hashtree_size = 512_KiB;
-    constexpr auto fec_size = partition_size - data_size - hashtree_size;
-
-    const auto block_size = manifest_.block_size();
-    SetSize(sys_, partition_size);
-    AddOperation(sys_, data_size);
-
-    sys_->set_estimate_cow_size(partition_size + data_size);
-
-    // Set hastree extents.
-    sys_->mutable_hash_tree_data_extent()->set_start_block(0);
-    sys_->mutable_hash_tree_data_extent()->set_num_blocks(data_size / block_size);
-
-    sys_->mutable_hash_tree_extent()->set_start_block(data_size / block_size);
-    sys_->mutable_hash_tree_extent()->set_num_blocks(hashtree_size / block_size);
-
-    // Set FEC extents.
-    sys_->mutable_fec_data_extent()->set_start_block(0);
-    sys_->mutable_fec_data_extent()->set_num_blocks((data_size + hashtree_size) / block_size);
-
-    sys_->mutable_fec_extent()->set_start_block((data_size + hashtree_size) / block_size);
-    sys_->mutable_fec_extent()->set_num_blocks(fec_size / block_size);
-
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Map and write some data to target partition.
-    ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-
-    // Finish update.
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Check that the target partition have the same content. Hashtree and FEC extents
-    // should be accounted for.
-    ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
-}
-
-// Test for overflow bit after update
-TEST_F(SnapshotUpdateTest, Overflow) {
-    if (IsCompressionEnabled()) {
-        GTEST_SKIP() << "No overflow bit set for userspace COWs";
-    }
-
-    const auto actual_write_size = GetSize(sys_);
-    const auto declared_write_size = actual_write_size - 1_MiB;
-
-    AddOperation(sys_, declared_write_size);
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Map and write some data to target partitions.
-    ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-
-    std::vector<android::dm::DeviceMapper::TargetInfo> table;
-    ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
-    ASSERT_EQ(1u, table.size());
-    EXPECT_TRUE(table[0].IsOverflowSnapshot());
-
-    ASSERT_FALSE(sm->FinishedSnapshotWrites(false))
-            << "FinishedSnapshotWrites should detect overflow of CoW device.";
-}
-
-TEST_F(SnapshotUpdateTest, LowSpace) {
-    static constexpr auto kMaxFree = 10_MiB;
-    auto userdata = std::make_unique<LowSpaceUserdata>();
-    ASSERT_TRUE(userdata->Init(kMaxFree));
-
-    // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
-    // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
-    constexpr uint64_t partition_size = 10_MiB;
-    SetSize(sys_, partition_size);
-    SetSize(vnd_, partition_size);
-    SetSize(prd_, partition_size);
-    sys_->set_estimate_cow_size(partition_size);
-    vnd_->set_estimate_cow_size(partition_size);
-    prd_->set_estimate_cow_size(partition_size);
-
-    AddOperationForPartitions();
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    auto res = sm->CreateUpdateSnapshots(manifest_);
-    ASSERT_FALSE(res);
-    ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
-    ASSERT_GE(res.required_size(), 14_MiB);
-    ASSERT_LT(res.required_size(), 40_MiB);
-}
-
-TEST_F(SnapshotUpdateTest, AddPartition) {
-    group_->add_partition_names("dlkm");
-
-    auto dlkm = manifest_.add_partitions();
-    dlkm->set_partition_name("dlkm");
-    dlkm->set_estimate_cow_size(2_MiB);
-    SetSize(dlkm, 3_MiB);
-
-    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
-    // fit in super, but not |prd|.
-    constexpr uint64_t partition_size = 3788_KiB;
-    SetSize(sys_, partition_size);
-    SetSize(vnd_, partition_size);
-    SetSize(prd_, partition_size);
-    SetSize(dlkm, partition_size);
-
-    AddOperationForPartitions({sys_, vnd_, prd_, dlkm});
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-
-    ASSERT_TRUE(init->EnsureSnapuserdConnected());
-    init->set_use_first_stage_snapuserd(true);
-
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Check that the target partitions have the same content.
-    std::vector<std::string> partitions = {"sys_b", "vnd_b", "prd_b", "dlkm_b"};
-    for (const auto& name : partitions) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
-    for (const auto& name : partitions) {
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init"));
-    }
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-
-    // Check that the target partitions have the same content after the merge.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name))
-                << "Content of " << name << " changes after the merge";
-    }
-}
-
-class AutoKill final {
-  public:
-    explicit AutoKill(pid_t pid) : pid_(pid) {}
-    ~AutoKill() {
-        if (pid_ > 0) kill(pid_, SIGKILL);
-    }
-
-    bool valid() const { return pid_ > 0; }
-
-  private:
-    pid_t pid_;
-};
-
-TEST_F(SnapshotUpdateTest, DaemonTransition) {
-    if (!IsCompressionEnabled()) {
-        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
-    }
-
-    // Ensure a connection to the second-stage daemon, but use the first-stage
-    // code paths thereafter.
-    ASSERT_TRUE(sm->EnsureSnapuserdConnected());
-    sm->set_use_first_stage_snapuserd(true);
-
-    AddOperationForPartitions();
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-    ASSERT_TRUE(UnmapAll());
-
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-
-    ASSERT_TRUE(init->EnsureSnapuserdConnected());
-    init->set_use_first_stage_snapuserd(true);
-
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    ASSERT_EQ(access("/dev/dm-user/sys_b-init", F_OK), 0);
-    ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), -1);
-
-    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
-
-    // :TODO: this is a workaround to ensure the handler list stays empty. We
-    // should make this test more like actual init, and spawn two copies of
-    // snapuserd, given how many other tests we now have for normal snapuserd.
-    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-init"));
-    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-init"));
-    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-init"));
-
-    // The control device should have been renamed.
-    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-init", 10s));
-    ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), 0);
-}
-
-TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
-    AddOperationForPartitions();
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-    ASSERT_TRUE(sm->MapAllSnapshots(10s));
-
-    // Read bytes back and verify they match the cache.
-    ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
-
-    ASSERT_TRUE(sm->UnmapAllSnapshots());
-}
-
-TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) {
-    AddOperationForPartitions();
-
-    // Execute the update from B->A.
-    test_device->set_slot_suffix("_b");
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    ASSERT_TRUE(UnmapAll());
-    std::string path;
-    ASSERT_TRUE(CreateLogicalPartition(
-            CreateLogicalPartitionParams{
-                    .block_device = fake_super,
-                    .metadata_slot = 0,
-                    .partition_name = "sys_a",
-                    .timeout_ms = 1s,
-                    .partition_opener = opener_.get(),
-            },
-            &path));
-
-    // Switch back to "A", make sure we can cancel. Instead of unmapping sys_a
-    // we should simply delete the old snapshots.
-    test_device->set_slot_suffix("_a");
-    ASSERT_TRUE(sm->BeginUpdate());
-}
-
-class FlashAfterUpdateTest : public SnapshotUpdateTest,
-                             public WithParamInterface<std::tuple<uint32_t, bool>> {
-  public:
-    AssertionResult InitiateMerge(const std::string& slot_suffix) {
-        auto sm = SnapshotManager::New(new TestDeviceInfo(fake_super, slot_suffix));
-        if (!sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)) {
-            return AssertionFailure() << "Cannot CreateLogicalAndSnapshotPartitions";
-        }
-        if (!sm->InitiateMerge()) {
-            return AssertionFailure() << "Cannot initiate merge";
-        }
-        return AssertionSuccess();
-    }
-};
-
-TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(MapUpdateSnapshots());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    bool after_merge = std::get<1>(GetParam());
-    if (after_merge) {
-        ASSERT_TRUE(InitiateMerge("_b"));
-        // Simulate shutting down the device after merge has initiated.
-        ASSERT_TRUE(UnmapAll());
-    }
-
-    auto flashed_slot = std::get<0>(GetParam());
-    auto flashed_slot_suffix = SlotSuffixForSlotNumber(flashed_slot);
-
-    // Simulate flashing |flashed_slot|. This clears the UPDATED flag.
-    auto flashed_builder = MetadataBuilder::New(*opener_, "super", flashed_slot);
-    ASSERT_NE(flashed_builder, nullptr);
-    flashed_builder->RemoveGroupAndPartitions(group_->name() + flashed_slot_suffix);
-    flashed_builder->RemoveGroupAndPartitions(kCowGroupName);
-    ASSERT_TRUE(FillFakeMetadata(flashed_builder.get(), manifest_, flashed_slot_suffix));
-
-    // Deliberately remove a partition from this build so that
-    // InitiateMerge do not switch state to "merging". This is possible in
-    // practice because the list of dynamic partitions may change.
-    ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix));
-    flashed_builder->RemovePartition("prd" + flashed_slot_suffix);
-
-    // Note that fastbootd always updates the partition table of both slots.
-    auto flashed_metadata = flashed_builder->Export();
-    ASSERT_NE(nullptr, flashed_metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 0));
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 1));
-
-    std::string path;
-    for (const auto& name : {"sys", "vnd"}) {
-        ASSERT_TRUE(CreateLogicalPartition(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = flashed_slot,
-                        .partition_name = name + flashed_slot_suffix,
-                        .timeout_ms = 1s,
-                        .partition_opener = opener_.get(),
-                },
-                &path));
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name + flashed_slot_suffix] = *hash;
-    }
-
-    // Simulate shutting down the device after flash.
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate reboot. After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount(flashed_slot_suffix);
-    ASSERT_NE(init, nullptr);
-
-    if (flashed_slot && after_merge) {
-        ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    }
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys", "vnd"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name + flashed_slot_suffix));
-    }
-
-    // There should be no snapshot to merge.
-    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
-    if (flashed_slot == 0 && after_merge) {
-        ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
-    } else {
-        // update_engine calls ProcessUpdateState first -- should see Cancelled.
-        ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
-    }
-
-    // Next OTA calls CancelUpdate no matter what.
-    ASSERT_TRUE(new_sm->CancelUpdate());
-}
-
-INSTANTIATE_TEST_SUITE_P(Snapshot, FlashAfterUpdateTest, Combine(Values(0, 1), Bool()),
-                         [](const TestParamInfo<FlashAfterUpdateTest::ParamType>& info) {
-                             return "Flash"s + (std::get<0>(info.param) ? "New"s : "Old"s) +
-                                    "Slot"s + (std::get<1>(info.param) ? "After"s : "Before"s) +
-                                    "Merge"s;
-                         });
-
-// Test behavior of ImageManager::Create on low space scenario. These tests assumes image manager
-// uses /data as backup device.
-class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
-  protected:
-    void SetUp() override {
-        SKIP_IF_NON_VIRTUAL_AB();
-        SnapshotTest::SetUp();
-        userdata_ = std::make_unique<LowSpaceUserdata>();
-        ASSERT_TRUE(userdata_->Init(GetParam()));
-    }
-    void TearDown() override {
-        RETURN_IF_NON_VIRTUAL_AB();
-        return;  // BUG(149738928)
-
-        EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
-                    image_manager_->DeleteBackingImage(kImageName));
-    }
-    static constexpr const char* kImageName = "my_image";
-    std::unique_ptr<LowSpaceUserdata> userdata_;
-};
-
-TEST_P(ImageManagerTest, CreateImageEnoughAvailSpace) {
-    if (userdata_->available_space() == 0) {
-        GTEST_SKIP() << "/data is full (" << userdata_->available_space()
-                     << " bytes available), skipping";
-    }
-    ASSERT_TRUE(image_manager_->CreateBackingImage(kImageName, userdata_->available_space(),
-                                                   IImageManager::CREATE_IMAGE_DEFAULT))
-            << "Should be able to create image with size = " << userdata_->available_space()
-            << " bytes";
-    ASSERT_TRUE(image_manager_->DeleteBackingImage(kImageName))
-            << "Should be able to delete created image";
-}
-
-TEST_P(ImageManagerTest, CreateImageNoSpace) {
-    uint64_t to_allocate = userdata_->free_space() + userdata_->bsize();
-    auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
-                                                  IImageManager::CREATE_IMAGE_DEFAULT);
-    ASSERT_FALSE(res) << "Should not be able to create image with size = " << to_allocate
-                      << " bytes because only " << userdata_->free_space() << " bytes are free";
-    ASSERT_EQ(FiemapStatus::ErrorCode::NO_SPACE, res.error_code()) << res.string();
-}
-
-std::vector<uint64_t> ImageManagerTestParams() {
-    std::vector<uint64_t> ret;
-    for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
-        ret.push_back(size);
-    }
-    return ret;
-}
-
-INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
-
-bool Mkdir(const std::string& path) {
-    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
-        std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
-        return false;
-    }
-    return true;
-}
-
-class SnapshotTestEnvironment : public ::testing::Environment {
-  public:
-    ~SnapshotTestEnvironment() override {}
-    void SetUp() override;
-    void TearDown() override;
-
-  private:
-    bool CreateFakeSuper();
-
-    std::unique_ptr<IImageManager> super_images_;
-};
-
-bool SnapshotTestEnvironment::CreateFakeSuper() {
-    // Create and map the fake super partition.
-    static constexpr int kImageFlags =
-            IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
-    if (!super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
-        LOG(ERROR) << "Could not create fake super partition";
-        return false;
-    }
-    if (!super_images_->MapImageDevice("fake-super", 10s, &fake_super)) {
-        LOG(ERROR) << "Could not map fake super partition";
-        return false;
-    }
-    test_device->set_fake_super(fake_super);
-    return true;
-}
-
-void SnapshotTestEnvironment::SetUp() {
-    // b/163082876: GTEST_SKIP in Environment will make atest report incorrect results. Until
-    // that is fixed, don't call GTEST_SKIP here, but instead call GTEST_SKIP in individual test
-    // suites.
-    RETURN_IF_NON_VIRTUAL_AB_MSG("Virtual A/B is not enabled, skipping global setup.\n");
-
-    std::vector<std::string> paths = {
-            // clang-format off
-            "/data/gsi/ota/test",
-            "/data/gsi/ota/test/super",
-            "/metadata/gsi/ota/test",
-            "/metadata/gsi/ota/test/super",
-            "/metadata/ota/test",
-            "/metadata/ota/test/snapshots",
-            // clang-format on
-    };
-    for (const auto& path : paths) {
-        ASSERT_TRUE(Mkdir(path));
-    }
-
-    // Create this once, otherwise, gsid will start/stop between each test.
-    test_device = new TestDeviceInfo();
-    sm = SnapshotManager::New(test_device);
-    ASSERT_NE(nullptr, sm) << "Could not create snapshot manager";
-
-    // Use a separate image manager for our fake super partition.
-    super_images_ = IImageManager::Open("ota/test/super", 10s);
-    ASSERT_NE(nullptr, super_images_) << "Could not create image manager";
-
-    // Map the old image if one exists so we can safely unmap everything that
-    // depends on it.
-    bool recreate_fake_super;
-    if (super_images_->BackingImageExists("fake-super")) {
-        if (super_images_->IsImageMapped("fake-super")) {
-            ASSERT_TRUE(super_images_->GetMappedImageDevice("fake-super", &fake_super));
-        } else {
-            ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super));
-        }
-        test_device->set_fake_super(fake_super);
-        recreate_fake_super = true;
-    } else {
-        ASSERT_TRUE(CreateFakeSuper());
-        recreate_fake_super = false;
-    }
-
-    // Clean up previous run.
-    MetadataMountedTest().TearDown();
-    SnapshotUpdateTest().Cleanup();
-    SnapshotTest().Cleanup();
-
-    if (recreate_fake_super) {
-        // Clean up any old copy.
-        DeleteBackingImage(super_images_.get(), "fake-super");
-        ASSERT_TRUE(CreateFakeSuper());
-    }
-}
-
-void SnapshotTestEnvironment::TearDown() {
-    RETURN_IF_NON_VIRTUAL_AB();
-    if (super_images_ != nullptr) {
-        DeleteBackingImage(super_images_.get(), "fake-super");
-    }
-}
-
-}  // namespace snapshot
-}  // namespace android
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
-
-    android::base::SetProperty("ctl.stop", "snapuserd");
-    android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
-
-    return RUN_ALL_TESTS();
-}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index d631d7a..eccb902 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -192,7 +192,6 @@
            lhs.nonremovable == rhs.nonremovable &&
            lhs.vold_managed == rhs.vold_managed &&
            lhs.recovery_only == rhs.recovery_only &&
-           lhs.verify == rhs.verify &&
            lhs.no_emulated_sd == rhs.no_emulated_sd &&
            lhs.no_trim == rhs.no_trim &&
            lhs.file_encryption == rhs.file_encryption &&
@@ -200,7 +199,6 @@
            lhs.slot_select == rhs.slot_select &&
            lhs.late_mount == rhs.late_mount &&
            lhs.no_fail == rhs.no_fail &&
-           lhs.verify_at_boot == rhs.verify_at_boot &&
            lhs.quota == rhs.quota &&
            lhs.avb == rhs.avb &&
            lhs.logical == rhs.logical &&
@@ -409,7 +407,7 @@
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
-source none0       swap   defaults      wait,check,nonremovable,recoveryonly,verifyatboot,verify
+source none0       swap   defaults      wait,check,nonremovable,recoveryonly
 source none1       swap   defaults      avb,noemulatedsd,notrim,formattable,nofail
 source none2       swap   defaults      first_stage_mount,latemount,quota,logical
 source none3       swap   defaults      checkpoint=block
@@ -430,8 +428,6 @@
         flags.check = true;
         flags.nonremovable = true;
         flags.recovery_only = true;
-        flags.verify_at_boot = true;
-        flags.verify = true;
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
 
diff --git a/healthd/Android.bp b/healthd/Android.bp
index eaa8e5b..24777c8 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -216,8 +216,6 @@
 
     shared_libs: [
         // common
-        "android.hardware.health@2.0",
-        "android.hardware.health@2.1",
         "libbase",
         "libcutils",
         "libhidlbase",
@@ -255,6 +253,10 @@
         "charger.cpp",
         "charger_utils.cpp",
     ],
+    shared_libs: [
+        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
+    ],
 
     target: {
         recovery: {
@@ -280,6 +282,11 @@
     name: "charger_test",
     defaults: ["charger_defaults"],
     srcs: ["charger_test.cpp"],
+    static_libs: [
+        "android.hardware.health@1.0",
+        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
+    ],
 }
 
 cc_test {
@@ -290,6 +297,9 @@
         "healthd_mode_charger_test.cpp"
     ],
     static_libs: [
+        "android.hardware.health@1.0",
+        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
         "libgmock",
     ],
     test_suites: [
diff --git a/healthd/OWNERS b/healthd/OWNERS
index d3f8758..e64c33d 100644
--- a/healthd/OWNERS
+++ b/healthd/OWNERS
@@ -1,2 +1 @@
 elsk@google.com
-hridya@google.com
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8045c71..cc445be 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -88,6 +88,7 @@
 using namespace std::literals::string_literals;
 
 using android::base::Basename;
+using android::base::ResultError;
 using android::base::SetProperty;
 using android::base::Split;
 using android::base::StartsWith;
@@ -116,7 +117,7 @@
                         android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
 
     template <typename T>
-    operator android::base::expected<T, ResultError>() {
+    operator android::base::expected<T, ResultError<int>>() {
         if (ignore_error_) {
             return {};
         }
@@ -130,7 +131,7 @@
     }
 
   private:
-    Error error_;
+    Error<> error_;
     bool ignore_error_;
 };
 
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index ada5a47..042988e 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -191,8 +191,6 @@
     auto& dm = android::dm::DeviceMapper::Instance();
     if (dm.GetState("vroot") != android::dm::DmDeviceState::INVALID) {
         root_entry->fs_mgr_flags.avb = true;
-    } else {
-        root_entry->fs_mgr_flags.verify = true;
     }
     return true;
 }
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 6eaa80f..7aa4a9d 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -297,7 +297,7 @@
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
         auto& failure = subcontext_reply->failure();
-        return ResultError(failure.error_string(), failure.error_errno());
+        return ResultError<>(failure.error_string(), failure.error_errno());
     }
 
     if (subcontext_reply->reply_case() != SubcontextReply::kSuccess) {
@@ -321,7 +321,7 @@
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
         auto& failure = subcontext_reply->failure();
-        return ResultError(failure.error_string(), failure.error_errno());
+        return ResultError<>(failure.error_string(), failure.error_errno());
     }
 
     if (subcontext_reply->reply_case() != SubcontextReply::kExpandArgsReply) {
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index fc3cdfb..1ac6d8e 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -99,7 +99,7 @@
     const char* const contexts[] = {
         "u:object_r:audio_device:s0",
         "u:object_r:sensors_device:s0",
-        "u:object_r:video_device:s0"
+        "u:object_r:video_device:s0",
         "u:object_r:zero_device:s0",
     };
 
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 0d9f2c7..c8bfb01 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -156,7 +156,6 @@
     },
     srcs: [
         "config_utils.cpp",
-        "canned_fs_config.cpp",
         "iosched_policy.cpp",
         "load_file.cpp",
         "native_handle.cpp",
@@ -173,6 +172,7 @@
         not_windows: {
             srcs: libcutils_nonwindows_sources + [
                 "ashmem-host.cpp",
+                "canned_fs_config.cpp",
                 "fs_config.cpp",
                 "trace-host.cpp",
             ],
@@ -193,6 +193,7 @@
             srcs: libcutils_nonwindows_sources + [
                 "android_reboot.cpp",
                 "ashmem-dev.cpp",
+                "canned_fs_config.cpp",
                 "fs_config.cpp",
                 "klog.cpp",
                 "partition_utils.cpp",
diff --git a/libcutils/canned_fs_config.cpp b/libcutils/canned_fs_config.cpp
index 2772ef0..b677949 100644
--- a/libcutils/canned_fs_config.cpp
+++ b/libcutils/canned_fs_config.cpp
@@ -18,6 +18,8 @@
 #include <private/canned_fs_config.h>
 #include <private/fs_config.h>
 
+#include <android-base/strings.h>
+
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -25,104 +27,107 @@
 #include <stdlib.h>
 #include <string.h>
 
-typedef struct {
-    const char* path;
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+using android::base::ConsumePrefix;
+using android::base::StartsWith;
+using android::base::Tokenize;
+
+struct Entry {
+    std::string path;
     unsigned uid;
     unsigned gid;
     unsigned mode;
     uint64_t capabilities;
-} Path;
+};
 
-static Path* canned_data = NULL;
-static int canned_alloc = 0;
-static int canned_used = 0;
-
-static int path_compare(const void* a, const void* b) {
-    return strcmp(((Path*)a)->path, ((Path*)b)->path);
-}
+static std::vector<Entry> canned_data;
 
 int load_canned_fs_config(const char* fn) {
-    char buf[PATH_MAX + 200];
-    FILE* f;
-
-    f = fopen(fn, "r");
-    if (f == NULL) {
-        fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
-        return -1;
-    }
-
-    while (fgets(buf, sizeof(buf), f)) {
-        Path* p;
-        char* token;
-        char* line = buf;
-        bool rootdir;
-
-        while (canned_used >= canned_alloc) {
-            canned_alloc = (canned_alloc+1) * 2;
-            canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
+    std::ifstream input(fn);
+    for (std::string line; std::getline(input, line);) {
+        // Historical: the root dir can be represented as a space character.
+        // e.g. " 1000 1000 0755" is parsed as
+        // path = " ", uid = 1000, gid = 1000, mode = 0755.
+        // But at the same time, we also have accepted
+        // "/ 1000 1000 0755".
+        if (StartsWith(line, " ")) {
+            line.insert(line.begin(), '/');
         }
-        p = canned_data + canned_used;
-        if (line[0] == '/') line++;
-        rootdir = line[0] == ' ';
-        p->path = strdup(rootdir ? "" : strtok(line, " "));
-        p->uid = atoi(strtok(rootdir ? line : NULL, " "));
-        p->gid = atoi(strtok(NULL, " "));
-        p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
-        p->capabilities = 0;
 
-        do {
-            token = strtok(NULL, " ");
-            if (token && strncmp(token, "capabilities=", 13) == 0) {
-                p->capabilities = strtoll(token+13, NULL, 0);
+        std::vector<std::string> tokens = Tokenize(line, " ");
+        if (tokens.size() < 4) {
+            std::cerr << "Ill-formed line: " << line << " in " << fn << std::endl;
+            return -1;
+        }
+
+        // Historical: remove the leading '/' if exists.
+        std::string path(tokens[0].front() == '/' ? std::string(tokens[0], 1) : tokens[0]);
+
+        Entry e{
+                .path = std::move(path),
+                .uid = static_cast<unsigned int>(atoi(tokens[1].c_str())),
+                .gid = static_cast<unsigned int>(atoi(tokens[2].c_str())),
+                // mode is in octal
+                .mode = static_cast<unsigned int>(strtol(tokens[3].c_str(), nullptr, 8)),
+                .capabilities = 0,
+        };
+
+        for (size_t i = 4; i < tokens.size(); i++) {
+            std::string_view sv = tokens[i];
+            if (ConsumePrefix(&sv, "capabilities=")) {
+                e.capabilities = strtoll(std::string(sv).c_str(), nullptr, 0);
                 break;
             }
-        } while (token);
+            // Historical: there can be tokens like "selabel=..." here. They have been ignored.
+            // It's not an error because selabels are applied separately in e2fsdroid using the
+            // file_contexts files set via -S option.
+            std::cerr << "info: ignored token \"" << sv << "\" in " << fn << std::endl;
+        }
 
-        canned_used++;
+        canned_data.emplace_back(std::move(e));
     }
 
-    fclose(f);
+    // Note: we used to sort the entries by path names. This was to improve the lookup performance
+    // by doing binary search. However, this is no longer the case. The lookup performance is not
+    // critical because this tool runs on the host, not on the device. Now, there can be multiple
+    // entries for the same path. Then the one that comes the last wins. This is to allow overriding
+    // platform provided fs_config with a user provided fs_config by appending the latter to the
+    // former.
+    //
+    // To implement the strategy, reverse the entries order, and search from the top.
+    std::reverse(canned_data.begin(), canned_data.end());
 
-    qsort(canned_data, canned_used, sizeof(Path), path_compare);
-    printf("loaded %d fs_config entries\n", canned_used);
-
+    std::cout << "loaded " << canned_data.size() << " fs_config entries" << std::endl;
     return 0;
 }
 
-static const int kDebugCannedFsConfig = 0;
+void canned_fs_config(const char* path, [[maybe_unused]] int dir,
+                      [[maybe_unused]] const char* target_out_path, unsigned* uid, unsigned* gid,
+                      unsigned* mode, uint64_t* capabilities) {
+    if (path != nullptr && path[0] == '/') path++;  // canned paths lack the leading '/'
 
-void canned_fs_config(const char* path, int dir, const char* target_out_path,
-                      unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
-    Path key, *p;
+    const Entry* found = nullptr;
+    // canned_data is already reversed. First match wins.
+    for (const auto& entry : canned_data) {
+        if (path == entry.path) {
+            found = &entry;
+            break;
+        }
+        continue;
+    }
 
-    key.path = path;
-    if (path[0] == '/') key.path++; // canned paths lack the leading '/'
-    p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
-    if (p == NULL) {
-        fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
+    if (found == nullptr) {
+        std::cerr << "failed to find " << path << " in canned fs_config" << std::endl;
         exit(1);
     }
-    *uid = p->uid;
-    *gid = p->gid;
-    *mode = p->mode;
-    *capabilities = p->capabilities;
 
-    if (kDebugCannedFsConfig) {
-        // for debugging, run the built-in fs_config and compare the results.
-
-        unsigned c_uid, c_gid, c_mode;
-        uint64_t c_capabilities;
-
-        fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
-
-        if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
-        if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
-        if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
-        if (c_capabilities != *capabilities) {
-            printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
-                path,
-                *capabilities,
-                c_capabilities);
-        }
-    }
+    *uid = found->uid;
+    *gid = found->gid;
+    *mode = found->mode;
+    *capabilities = found->capabilities;
 }
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 24c6ae6..97e93f8 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -208,6 +208,39 @@
 }
 
 /**
+ * Trace an instantaneous context. name is used to identify the context.
+ *
+ * An "instant" is an event with no defined duration. Visually is displayed like a single marker
+ * in the timeline (rather than a span, in the case of begin/end events).
+ *
+ * By default, instant events are added into a dedicated track that has the same name of the event.
+ * Use atrace_instant_for_track to put different instant events into the same timeline track/row.
+ */
+#define ATRACE_INSTANT(name) atrace_instant(ATRACE_TAG, name)
+static inline void atrace_instant(uint64_t tag, const char* name) {
+    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+        void atrace_instant_body(const char*);
+        atrace_instant_body(name);
+    }
+}
+
+/**
+ * Trace an instantaneous context. name is used to identify the context.
+ * trackName is the name of the row where the event should be recorded.
+ *
+ * An "instant" is an event with no defined duration. Visually is displayed like a single marker
+ * in the timeline (rather than a span, in the case of begin/end events).
+ */
+#define ATRACE_INSTANT_FOR_TRACK(trackName, name) \
+    atrace_instant_for_track(ATRACE_TAG, trackName, name)
+static inline void atrace_instant_for_track(uint64_t tag, const char* trackName, const char* name) {
+    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+        void atrace_instant_for_track_body(const char*, const char*);
+        atrace_instant_for_track_body(trackName, name);
+    }
+}
+
+/**
  * Traces an integer counter value.  name is used to identify the counter.
  * This can be used to track how a value changes over time.
  */
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e65fe92..23d1415 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -131,6 +131,7 @@
 #define AID_ARTD 1082             /* ART Service daemon */
 #define AID_UWB 1083              /* UWB subsystem */
 #define AID_THREAD_NETWORK 1084   /* Thread Network subsystem */
+#define AID_DICED 1085            /* Android's DICE daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
@@ -158,6 +159,7 @@
 #define AID_READPROC 3009     /* Allow /proc read access */
 #define AID_WAKELOCK 3010     /* Allow system wakelock read/write access */
 #define AID_UHID 3011         /* Allow read/write to /dev/uhid node */
+#define AID_READTRACEFS 3012  /* Allow tracefs read */
 
 /* The range 5000-5999 is also reserved for vendor partition. */
 #define AID_OEM_RESERVED_2_START 5000
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index f7eed48..ef7c72d 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -217,6 +217,28 @@
     WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
 }
 
+void atrace_instant_body(const char* name) {
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("I", "|", "%s", name, "");
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("I|%d|", "%s", name, "");
+}
+
+void atrace_instant_for_track_body(const char* trackName, const char* name) {
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("N", "|", "|%s", trackName, name);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("N|%d|", "|%s", name, trackName);
+}
+
 void atrace_int_body(const char* name, int32_t value)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 1ab63dc..25c86f4 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -89,6 +89,14 @@
     WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
 }
 
+void atrace_instant_body(const char* name) {
+    WRITE_MSG("I|%d|", "%s", name, "");
+}
+
+void atrace_instant_for_track_body(const char* trackName, const char* name) {
+    WRITE_MSG("N|%d|", "|%s", trackName, name);
+}
+
 void atrace_int_body(const char* name, int32_t value)
 {
     WRITE_MSG("C|%d|", "|%" PRId32, name, value);
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index 832b36a..ff6d202 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -195,6 +195,106 @@
   ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
+TEST_F(TraceDevTest, atrace_instant_body_normal) {
+    atrace_instant_body("fake_name");
+
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    std::string expected = android::base::StringPrintf("I|%d|fake_name", getpid());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_body_exact) {
+    std::string expected = android::base::StringPrintf("I|%d|", getpid());
+    std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
+    atrace_instant_body(name.c_str());
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    expected += name;
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+    // Add a single character and verify we get the exact same value as before.
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    name += '*';
+    atrace_instant_body(name.c_str());
+    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_body_truncated) {
+    std::string expected = android::base::StringPrintf("I|%d|", getpid());
+    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_instant_body(name.c_str());
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+    expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_normal) {
+    atrace_instant_for_track_body("fake_track", "fake_name");
+
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    std::string expected = android::base::StringPrintf("N|%d|fake_track|fake_name", getpid());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_exact) {
+    const int nameSize = 5;
+    std::string expected = android::base::StringPrintf("N|%d|", getpid());
+    std::string trackName = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize);
+    atrace_instant_for_track_body(trackName.c_str(), "name");
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    expected += trackName + "|name";
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+    // Add a single character and verify we get the exact same value as before.
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    trackName += '*';
+    atrace_instant_for_track_body(trackName.c_str(), "name");
+    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated) {
+    const int nameSize = 5;
+    std::string expected = android::base::StringPrintf("N|%d|", getpid());
+    std::string trackName = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_instant_for_track_body(trackName.c_str(), "name");
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize;
+    expected += android::base::StringPrintf("%.*s|name", expected_len, trackName.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
 TEST_F(TraceDevTest, atrace_int_body_normal) {
   atrace_int_body("fake_name", 12345);
 
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index 9781ad3..b01a0ec 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -28,6 +28,9 @@
 void atrace_end_body() { }
 void atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}
 void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_instant_body(const char* /*name*/) {}
+void atrace_instant_for_track_body(const char* /*trackName*/,
+                                   const char* /*name*/) {}
 void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
 void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
 void atrace_init() {}
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 2864ad0..02bd2e3 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -23,7 +23,6 @@
     },
 
     srcs: [
-        "checksum.c",
         "dhcpclient.c",
         "dhcpmsg.c",
         "ifc_utils.c",
@@ -35,6 +34,10 @@
         "liblog",
     ],
 
+    static_libs: [
+        "libip_checksum",
+    ],
+
     cflags: ["-Werror"],
 
     export_include_dirs: ["include"],
@@ -45,21 +48,6 @@
     ],
 }
 
-cc_library_static {
-    name: "libipchecksum",
-
-    srcs: [
-        "checksum.c",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    export_include_dirs: ["include"],
-}
-
 cc_binary {
     name: "dhcpdbg",
 
diff --git a/libnetutils/checksum.c b/libnetutils/checksum.c
deleted file mode 100644
index 74b5fdd..0000000
--- a/libnetutils/checksum.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 2011 Daniel Drown
- *
- * 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.
- *
- * checksum.c - ipv4/ipv6 checksum calculation
- */
-#include <netinet/icmp6.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
-
-#include "netutils/checksum.h"
-
-/* function: ip_checksum_add
- * adds data to a checksum. only known to work on little-endian hosts
- * current - the current checksum (or 0 to start a new checksum)
- *   data        - the data to add to the checksum
- *   len         - length of data
- */
-uint32_t ip_checksum_add(uint32_t current, const void* data, int len) {
-    uint32_t checksum = current;
-    int left = len;
-    const uint16_t* data_16 = data;
-
-    while (left > 1) {
-        checksum += *data_16;
-        data_16++;
-        left -= 2;
-    }
-    if (left) {
-        checksum += *(uint8_t*)data_16;
-    }
-
-    return checksum;
-}
-
-/* function: ip_checksum_fold
- * folds a 32-bit partial checksum into 16 bits
- *   temp_sum - sum from ip_checksum_add
- *   returns: the folded checksum in network byte order
- */
-uint16_t ip_checksum_fold(uint32_t temp_sum) {
-    while (temp_sum > 0xffff) {
-        temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
-    }
-    return temp_sum;
-}
-
-/* function: ip_checksum_finish
- * folds and closes the checksum
- *   temp_sum - sum from ip_checksum_add
- *   returns: a header checksum value in network byte order
- */
-uint16_t ip_checksum_finish(uint32_t temp_sum) {
-    return ~ip_checksum_fold(temp_sum);
-}
-
-/* function: ip_checksum
- * combined ip_checksum_add and ip_checksum_finish
- *   data - data to checksum
- *   len  - length of data
- */
-uint16_t ip_checksum(const void* data, int len) {
-    // TODO: consider starting from 0xffff so the checksum of a buffer entirely consisting of zeros
-    // is correctly calculated as 0.
-    uint32_t temp_sum;
-
-    temp_sum = ip_checksum_add(0, data, len);
-    return ip_checksum_finish(temp_sum);
-}
-
-/* function: ipv6_pseudo_header_checksum
- * calculate the pseudo header checksum for use in tcp/udp/icmp headers
- *   ip6      - the ipv6 header
- *   len      - the transport length (transport header + payload)
- *   protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
- */
-uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol) {
-    uint32_t checksum_len = htonl(len);
-    uint32_t checksum_next = htonl(protocol);
-
-    uint32_t current = 0;
-
-    current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
-    current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
-    current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
-    current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
-
-    return current;
-}
-
-/* function: ipv4_pseudo_header_checksum
- * calculate the pseudo header checksum for use in tcp/udp headers
- *   ip      - the ipv4 header
- *   len     - the transport length (transport header + payload)
- */
-uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len) {
-    uint16_t temp_protocol, temp_length;
-
-    temp_protocol = htons(ip->protocol);
-    temp_length = htons(len);
-
-    uint32_t current = 0;
-
-    current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
-    current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
-    current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
-    current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
-
-    return current;
-}
-
-/* function: ip_checksum_adjust
- * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
- *   checksum    - the header checksum in the original packet in network byte order
- *   old_hdr_sum - the pseudo-header checksum of the original packet
- *   new_hdr_sum - the pseudo-header checksum of the translated packet
- *   returns: the new header checksum in network byte order
- */
-uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
-    // Algorithm suggested in RFC 1624.
-    // http://tools.ietf.org/html/rfc1624#section-3
-    checksum = ~checksum;
-    uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
-    uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
-    if (folded_sum > folded_old) {
-        return ~(folded_sum - folded_old);
-    } else {
-        return ~(folded_sum - folded_old - 1);  // end-around borrow
-    }
-}
diff --git a/libnetutils/include/netutils/checksum.h b/libnetutils/include/netutils/checksum.h
deleted file mode 100644
index 868217c..0000000
--- a/libnetutils/include/netutils/checksum.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2011 Daniel Drown
- *
- * 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.
- *
- * checksum.h - checksum functions
- */
-#ifndef __CHECKSUM_H__
-#define __CHECKSUM_H__
-
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <stdint.h>
-
-uint32_t ip_checksum_add(uint32_t current, const void* data, int len);
-uint16_t ip_checksum_finish(uint32_t temp_sum);
-uint16_t ip_checksum(const void* data, int len);
-
-uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol);
-uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len);
-
-uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
-
-#endif /* __CHECKSUM_H__ */
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 1a4196a..169b1d3 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -165,27 +165,7 @@
     return 0;
 }
 
-int get_sched_policy(int tid, SchedPolicy* policy) {
-    if (tid == 0) {
-        tid = GetThreadId();
-    }
-
-    std::string group;
-    if (schedboost_enabled()) {
-        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
-            (getCGroupSubsys(tid, "cpu", group) < 0)) {
-                LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
-                return -1;
-        }
-    }
-    if (group.empty() && cpusets_enabled()) {
-        if (getCGroupSubsys(tid, "cpuset", group) < 0) {
-            LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
-            return -1;
-        }
-    }
-
-    // TODO: replace hardcoded directories
+static int get_sched_policy_from_group(const std::string& group, SchedPolicy* policy) {
     if (group.empty()) {
         *policy = SP_FOREGROUND;
     } else if (group == "foreground") {
@@ -205,6 +185,35 @@
     return 0;
 }
 
+int get_sched_policy(int tid, SchedPolicy* policy) {
+    if (tid == 0) {
+        tid = GetThreadId();
+    }
+
+    std::string group;
+    if (schedboost_enabled()) {
+        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
+            (getCGroupSubsys(tid, "cpu", group) < 0)) {
+            LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
+            return -1;
+        }
+        // Wipe invalid group to fallback to cpuset
+        if (!group.empty()) {
+            if (get_sched_policy_from_group(group, policy) < 0) {
+                group.clear();
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    if (cpusets_enabled() && getCGroupSubsys(tid, "cpuset", group) < 0) {
+        LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
+        return -1;
+    }
+    return get_sched_policy_from_group(group, policy);
+}
+
 #else
 
 /* Stubs for non-Android targets. */
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index e935f99..3834f91 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -144,30 +144,13 @@
     return true;
 }
 
-bool SetCgroupAction::IsAppDependentPath(const std::string& path) {
-    return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
-}
-
-SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
-    : controller_(c), path_(p) {
-    // file descriptors for app-dependent paths can't be cached
-    if (IsAppDependentPath(path_)) {
-        // file descriptor is not cached
-        fd_.reset(FDS_APP_DEPENDENT);
-        return;
-    }
-
-    // file descriptor can be cached later on request
-    fd_.reset(FDS_NOT_CACHED);
-}
-
-void SetCgroupAction::EnableResourceCaching() {
+void CachedFdProfileAction::EnableResourceCaching() {
     std::lock_guard<std::mutex> lock(fd_mutex_);
     if (fd_ != FDS_NOT_CACHED) {
         return;
     }
 
-    std::string tasks_path = controller_.GetTasksFilePath(path_);
+    std::string tasks_path = GetPath();
 
     if (access(tasks_path.c_str(), W_OK) != 0) {
         // file is not accessible
@@ -185,7 +168,7 @@
     fd_ = std::move(fd);
 }
 
-void SetCgroupAction::DropResourceCaching() {
+void CachedFdProfileAction::DropResourceCaching() {
     std::lock_guard<std::mutex> lock(fd_mutex_);
     if (fd_ == FDS_NOT_CACHED) {
         return;
@@ -194,6 +177,26 @@
     fd_.reset(FDS_NOT_CACHED);
 }
 
+bool CachedFdProfileAction::IsAppDependentPath(const std::string& path) {
+    return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
+}
+
+void CachedFdProfileAction::InitFd(const std::string& path) {
+    // file descriptors for app-dependent paths can't be cached
+    if (IsAppDependentPath(path)) {
+        // file descriptor is not cached
+        fd_.reset(FDS_APP_DEPENDENT);
+        return;
+    }
+    // file descriptor can be cached later on request
+    fd_.reset(FDS_NOT_CACHED);
+}
+
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
+    : controller_(c), path_(p) {
+    InitFd(controller_.GetTasksFilePath(path_));
+}
+
 bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
     if (tid <= 0) {
         return true;
@@ -270,7 +273,7 @@
     std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
+        PLOG(WARNING) << "Failed to open " << tasks_path;
         return false;
     }
     if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
@@ -281,37 +284,73 @@
     return true;
 }
 
-bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
-    std::string filepath(filepath_), value(value_);
+WriteFileAction::WriteFileAction(const std::string& path, const std::string& value,
+                                 bool logfailures)
+    : path_(path), value_(value), logfailures_(logfailures) {
+    InitFd(path_);
+}
 
-    filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
-    filepath = StringReplace(filepath, "<pid>", std::to_string(pid), true);
-    value = StringReplace(value, "<uid>", std::to_string(uid), true);
-    value = StringReplace(value, "<pid>", std::to_string(pid), true);
+bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path,
+                                       bool logfailures) {
+    // Use WriteStringToFd instead of WriteStringToFile because the latter will open file with
+    // O_TRUNC which causes kernfs_mutex contention
+    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
 
-    if (!WriteStringToFile(value, filepath)) {
-        if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
+    if (tmp_fd < 0) {
+        if (logfailures) PLOG(WARNING) << "Failed to open " << path;
+        return false;
+    }
+
+    if (!WriteStringToFd(value, tmp_fd)) {
+        if (logfailures) PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
         return false;
     }
 
     return true;
 }
 
+bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    std::string value(value_);
+    std::string path(path_);
+
+    value = StringReplace(value, "<uid>", std::to_string(uid), true);
+    value = StringReplace(value, "<pid>", std::to_string(pid), true);
+    path = StringReplace(path, "<uid>", std::to_string(uid), true);
+    path = StringReplace(path, "<pid>", std::to_string(pid), true);
+
+    return WriteValueToFile(value, path, logfailures_);
+}
+
 bool WriteFileAction::ExecuteForTask(int tid) const {
-    std::string filepath(filepath_), value(value_);
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    std::string value(value_);
     int uid = getuid();
 
-    filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
-    filepath = StringReplace(filepath, "<pid>", std::to_string(tid), true);
     value = StringReplace(value, "<uid>", std::to_string(uid), true);
     value = StringReplace(value, "<pid>", std::to_string(tid), true);
 
-    if (!WriteStringToFile(value, filepath)) {
-        if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
+    if (IsFdValid()) {
+        // fd is cached, reuse it
+        if (!WriteStringToFd(value, fd_)) {
+            if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
+            return false;
+        }
+        return true;
+    }
+
+    if (fd_ == FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return true;
+    }
+
+    if (fd_ == FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
         return false;
     }
 
-    return true;
+    return WriteValueToFile(value, path_, logfailures_);
 }
 
 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 97c38f4..278892d 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -108,50 +108,67 @@
     std::string value_;
 };
 
-// Set cgroup profile element
-class SetCgroupAction : public ProfileAction {
+// Abstract profile element for cached fd
+class CachedFdProfileAction : public ProfileAction {
   public:
-    SetCgroupAction(const CgroupController& c, const std::string& p);
-
-    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
-    virtual bool ExecuteForTask(int tid) const;
     virtual void EnableResourceCaching();
     virtual void DropResourceCaching();
 
-    const CgroupController* controller() const { return &controller_; }
-    std::string path() const { return path_; }
-
-  private:
+  protected:
     enum FdState {
         FDS_INACCESSIBLE = -1,
         FDS_APP_DEPENDENT = -2,
         FDS_NOT_CACHED = -3,
     };
 
-    CgroupController controller_;
-    std::string path_;
     android::base::unique_fd fd_;
     mutable std::mutex fd_mutex_;
 
     static bool IsAppDependentPath(const std::string& path);
-    static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
 
+    void InitFd(const std::string& path);
     bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
+
+    virtual const std::string GetPath() const = 0;
 };
 
-// Write to file action
-class WriteFileAction : public ProfileAction {
+// Set cgroup profile element
+class SetCgroupAction : public CachedFdProfileAction {
   public:
-    WriteFileAction(const std::string& filepath, const std::string& value,
-                    bool logfailures) noexcept
-        : filepath_(filepath), value_(value), logfailures_(logfailures) {}
+    SetCgroupAction(const CgroupController& c, const std::string& p);
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
 
+    const CgroupController* controller() const { return &controller_; }
+
+  protected:
+    const std::string GetPath() const override { return controller_.GetTasksFilePath(path_); }
+
   private:
-    std::string filepath_, value_;
+    CgroupController controller_;
+    std::string path_;
+
+    static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+};
+
+// Write to file action
+class WriteFileAction : public CachedFdProfileAction {
+  public:
+    WriteFileAction(const std::string& path, const std::string& value, bool logfailures);
+
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
+
+  protected:
+    const std::string GetPath() const override { return path_; }
+
+  private:
+    std::string path_, value_;
     bool logfailures_;
+
+    static bool WriteValueToFile(const std::string& value, const std::string& path,
+                                 bool logfailures);
 };
 
 class TaskProfile {
diff --git a/libstats/bootstrap/Android.bp b/libstats/bootstrap/Android.bp
new file mode 100644
index 0000000..332d9c8
--- /dev/null
+++ b/libstats/bootstrap/Android.bp
@@ -0,0 +1,49 @@
+//
+// 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.
+//
+
+// =========================================================================
+// Native library that provide a client to StatsBootstrapAtomService.
+// This library should only be used by processes that start in the bootstrap namespace.
+// All other clients should use libstatssocket, provided by the statsd apex.
+// =========================================================================
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "libstatsbootstrap_defaults",
+    srcs: [
+        "BootstrapClientInternal.cpp",
+        "StatsBootstrapAtomClient.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "android.os.statsbootstrap_aidl-cpp",
+    ],
+}
+
+cc_library {
+    name: "libstatsbootstrap",
+    defaults: ["libstatsbootstrap_defaults"],
+    export_include_dirs: ["include"],
+}
+
+
diff --git a/libstats/bootstrap/BootstrapClientInternal.cpp b/libstats/bootstrap/BootstrapClientInternal.cpp
new file mode 100644
index 0000000..b02e116
--- /dev/null
+++ b/libstats/bootstrap/BootstrapClientInternal.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 "BootstrapClientInternal.h"
+
+#include <binder/IServiceManager.h>
+
+namespace android {
+namespace os {
+namespace stats {
+
+sp<BootstrapClientInternal> BootstrapClientInternal::getInstance() {
+    static sp<BootstrapClientInternal> client = new BootstrapClientInternal();
+    return client;
+}
+
+sp<IStatsBootstrapAtomService> BootstrapClientInternal::getServiceNonBlocking() {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mService != nullptr) {
+        return mService;
+    }
+    connectNonBlockingLocked();
+    return mService;
+}
+
+void BootstrapClientInternal::binderDied(const wp<IBinder>&) {
+    std::lock_guard<std::mutex> lock(mLock);
+    mService = nullptr;
+    connectNonBlockingLocked();
+}
+
+void BootstrapClientInternal::connectNonBlockingLocked() {
+    const String16 name("statsbootstrap");
+    mService =
+            interface_cast<IStatsBootstrapAtomService>(defaultServiceManager()->checkService(name));
+    if (mService != nullptr) {
+        // Set up binder death.
+        IInterface::asBinder(mService)->linkToDeath(this);
+    }
+}
+
+}  // namespace stats
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/libstats/bootstrap/BootstrapClientInternal.h b/libstats/bootstrap/BootstrapClientInternal.h
new file mode 100644
index 0000000..96238da
--- /dev/null
+++ b/libstats/bootstrap/BootstrapClientInternal.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/os/IStatsBootstrapAtomService.h>
+
+namespace android {
+namespace os {
+namespace stats {
+
+class BootstrapClientInternal : public IBinder::DeathRecipient {
+  public:
+    static sp<BootstrapClientInternal> getInstance();
+    void binderDied(const wp<IBinder>& who) override;
+    sp<IStatsBootstrapAtomService> getServiceNonBlocking();
+
+  private:
+    BootstrapClientInternal() {}
+    void connectNonBlockingLocked();
+
+    mutable std::mutex mLock;
+    sp<IStatsBootstrapAtomService> mService;
+};
+
+}  // namespace stats
+}  // namespace os
+}  // namespace android
diff --git a/libstats/bootstrap/StatsBootstrapAtomClient.cpp b/libstats/bootstrap/StatsBootstrapAtomClient.cpp
new file mode 100644
index 0000000..348b7fa
--- /dev/null
+++ b/libstats/bootstrap/StatsBootstrapAtomClient.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "include/StatsBootstrapAtomClient.h"
+
+#include <android/os/IStatsBootstrapAtomService.h>
+
+#include "BootstrapClientInternal.h"
+
+namespace android {
+namespace os {
+namespace stats {
+
+bool StatsBootstrapAtomClient::reportBootstrapAtom(const StatsBootstrapAtom& atom) {
+    sp<IStatsBootstrapAtomService> service =
+            BootstrapClientInternal::getInstance()->getServiceNonBlocking();
+    if (service == nullptr) {
+        return false;
+    }
+    return service->reportBootstrapAtom(atom).isOk();
+}
+
+}  // namespace stats
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/libstats/bootstrap/include/StatsBootstrapAtomClient.h b/libstats/bootstrap/include/StatsBootstrapAtomClient.h
new file mode 100644
index 0000000..87930fd
--- /dev/null
+++ b/libstats/bootstrap/include/StatsBootstrapAtomClient.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/os/StatsBootstrapAtom.h>
+
+namespace android {
+namespace os {
+namespace stats {
+
+class StatsBootstrapAtomClient {
+  public:
+    static bool reportBootstrapAtom(const StatsBootstrapAtom& atom);
+};
+
+}  // namespace stats
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/libsync/OWNERS b/libsync/OWNERS
index e75b15b..8f69e50 100644
--- a/libsync/OWNERS
+++ b/libsync/OWNERS
@@ -1,3 +1,2 @@
 chrisforbes@google.com
-hridya@google.com
 jessehall@google.com
diff --git a/libutils/Android.bp b/libutils/Android.bp
index e6d9a4c..bda9d6b 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -177,6 +177,8 @@
         "//apex_available:platform",
     ],
     min_sdk_version: "apex_inherit",
+
+    afdo: true,
 }
 
 cc_library {
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 6e293c7..3bf5779 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -317,10 +317,7 @@
     if (pri >= ANDROID_PRIORITY_BACKGROUND) {
         rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
     } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
-        SchedPolicy policy = SP_FOREGROUND;
-        // Change to the sched policy group of the process.
-        get_sched_policy(getpid(), &policy);
-        rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
+        rc = SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
     }
 
     if (rc) {
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 993d642..2ed9eec 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -115,6 +115,9 @@
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
 endif
+ifdef BOARD_USES_SYSTEM_DLKM_PARTITION
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
+endif
 
 # For /odm partition.
 LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
deleted file mode 100644
index cfc59a7..0000000
--- a/rootdir/avb/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
-    name: "q-gsi_avbpubkey",
-    srcs: [
-        "q-gsi.avbpubkey",
-    ],
-}
-
-filegroup {
-    name: "r-gsi_avbpubkey",
-    srcs: [
-        "r-gsi.avbpubkey",
-    ],
-}
-
-filegroup {
-    name: "s-gsi_avbpubkey",
-    srcs: [
-        "s-gsi.avbpubkey",
-    ],
-}
-
-filegroup {
-    name: "qcar-gsi_avbpubkey",
-    srcs: [
-        "qcar-gsi.avbpubkey",
-    ],
-}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b09c2f1..cd73498 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -78,8 +78,8 @@
     mkdir /dev/boringssl 0755 root root
     mkdir /dev/boringssl/selftest 0755 root root
 
-    # Mount tracefs
-    mount tracefs tracefs /sys/kernel/tracing
+    # Mount tracefs (with GID=AID_READTRACEFS)
+    mount tracefs tracefs /sys/kernel/tracing gid=3012
 
     # create sys dirctory
     mkdir /dev/sys 0755 system system
@@ -914,6 +914,9 @@
     exec - media_rw media_rw -- /system/bin/chattr +F /data/media
     mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
 
+    # Create directories for boot animation.
+    mkdir /data/bootanim 0755 system system encryption=None
+
     exec_start derive_sdk
 
     init_user0
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
index 0a26aba..52a7f74 100644
--- a/set-verity-state/set-verity-state.cpp
+++ b/set-verity-state/set-verity-state.cpp
@@ -244,25 +244,6 @@
       any_changed = true;
     }
     avb_ops_user_free(ops);
-  } else {
-    // Not using AVB - assume VB1.0.
-
-    // read all fstab entries at once from all sources
-    android::fs_mgr::Fstab fstab;
-    if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
-      printf("Failed to read fstab\n");
-      suggest_run_adb_root();
-      return 0;
-    }
-
-    // Loop through entries looking for ones that verity manages.
-    for (const auto& entry : fstab) {
-      if (entry.fs_mgr_flags.verify) {
-        if (set_verity_enabled_state(entry.blk_device.c_str(), entry.mount_point.c_str(), enable)) {
-          any_changed = true;
-        }
-      }
-    }
   }
   if (!any_changed) any_changed = overlayfs_setup(enable);
 
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 33500db..3e646e0 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -328,7 +328,7 @@
     vector<StorageInfo> halInfos;
     auto ret = mHealth->getStorageInfo(&halInfos);
     if (ret.isOk()) {
-        if (halInfos.size() == 0) {
+        if (halInfos.size() != 0) {
             set_values_from_hal_storage_info(halInfos[0]);
             publish();
             return;
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index 5f8524b..68a7912 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -91,7 +91,7 @@
 }  // namespace
 
 ScopedAStatus TrustyKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
-    info->versionNumber = 1;
+    info->versionNumber = 2;
     info->securityLevel = kSecurityLevel;
     info->keyMintName = "TrustyKeyMintDevice";
     info->keyMintAuthorName = "Google";
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
index d5a77fb..3447b27 100644
--- a/trusty/keymaster/keymint/service.cpp
+++ b/trusty/keymaster/keymint/service.cpp
@@ -41,7 +41,7 @@
 
 int main() {
     auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();
-    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_1);
+    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_2);
     if (err != 0) {
         LOG(FATAL) << "Could not initialize TrustyKeymaster for KeyMint (" << err << ")";
         return -1;