Merge "ld.config.txt: remove link from rs to vndk"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index a3bd44f..cc1978d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,6 +4,9 @@
       "name": "adbd_test"
     },
     {
+      "name": "CtsInitTestCases"
+    },
+    {
       "name": "debuggerd_test"
     },
     {
@@ -13,9 +16,6 @@
       "name": "fs_mgr_vendor_overlay_test"
     },
     {
-      "name": "init_tests"
-    },
-    {
       "name": "libbase_test"
     },
     {
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
index 6799c1f..330040d 100644
--- a/base/include/android-base/format.h
+++ b/base/include/android-base/format.h
@@ -18,8 +18,11 @@
 
 // We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
 // It is accessed through its normal fmt:: namespace.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshadow"
+#include <fmt/chrono.h>
+#pragma clang diagnostic pop
 #include <fmt/core.h>
 #include <fmt/format.h>
 #include <fmt/ostream.h>
 #include <fmt/printf.h>
-#include <fmt/time.h>
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 2e226da..0602e0a 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -194,6 +194,7 @@
 cc_test {
     name: "debuggerd_test",
     defaults: ["debuggerd_defaults"],
+    require_root: true,
 
     cflags: ["-Wno-missing-field-initializers"],
     srcs: [
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index 53ef01c..b4a1e71 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -2,10 +2,10 @@
     user tombstoned
     group system
 
-    # Don't start tombstoned until after the real /data is mounted.
-    class late_start
-
     socket tombstoned_crash seqpacket 0666 system system
     socket tombstoned_intercept seqpacket 0666 system system
     socket tombstoned_java_trace seqpacket 0666 system system
     writepid /dev/cpuset/system-background/tasks
+
+on post-fs-data
+    start tombstoned
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 409ef70..4c77c75 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -194,23 +194,6 @@
     return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
 }
 
-bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
-    if (args.size() < 2) {
-        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
-    }
-
-    if (GetDeviceLockStatus()) {
-        return device->WriteStatus(FastbootResult::FAIL,
-                                   "Flashing is not allowed on locked devices");
-    }
-
-    int ret = Flash(device, args[1]);
-    if (ret < 0) {
-        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
-    }
-    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
-}
-
 bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
@@ -440,6 +423,11 @@
     if (!partition) {
         return device->WriteFail("Partition does not exist");
     }
+
+    // Remove the updated flag to cancel any snapshots.
+    uint32_t attrs = partition->attributes();
+    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
+
     if (!builder->ResizePartition(partition, partition_size)) {
         return device->WriteFail("Not enough space to resize partition");
     }
@@ -449,6 +437,42 @@
     return device->WriteOkay("Partition resized");
 }
 
+void CancelPartitionSnapshot(FastbootDevice* device, const std::string& partition_name) {
+    PartitionBuilder builder(device, partition_name);
+    if (!builder.Valid()) return;
+
+    auto partition = builder->FindPartition(partition_name);
+    if (!partition) return;
+
+    // Remove the updated flag to cancel any snapshots.
+    uint32_t attrs = partition->attributes();
+    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
+
+    builder.Write();
+}
+
+bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Flashing is not allowed on locked devices");
+    }
+
+    const auto& partition_name = args[1];
+    if (LogicalPartitionExists(device, partition_name)) {
+        CancelPartitionSnapshot(device, partition_name);
+    }
+
+    int ret = Flash(device, partition_name);
+    if (ret < 0) {
+        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
+}
+
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteFail("Invalid arguments");
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 8923f40..5bd37e1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -160,6 +160,10 @@
                                                       "vbmeta_system",
                                                                   true,  ImageType::BootCritical },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
+    { "vendor_boot",
+                  "vendor_boot.img",  "vendor_boot.sig",
+                                                      "vendor_boot",
+                                                                  true,  ImageType::BootCritical },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 1b85b47..ea799ce 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -79,9 +79,9 @@
     return true;
 }
 
-static bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
-                          const LpMetadataPartition& partition, const std::string& super_device,
-                          DmTable* table) {
+bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
+                   const LpMetadataPartition& partition, const std::string& super_device,
+                   DmTable* table) {
     uint64_t sector = 0;
     for (size_t i = 0; i < partition.num_extents; i++) {
         const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -154,64 +154,91 @@
     return true;
 }
 
-bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path) {
-    const LpMetadata* metadata = params.metadata;
+bool CreateLogicalPartitionParams::InitDefaults(CreateLogicalPartitionParams::OwnedData* owned) {
+    if (block_device.empty()) {
+        LOG(ERROR) << "block_device is required for CreateLogicalPartition";
+        return false;
+    }
 
     // Read metadata if needed.
-    std::unique_ptr<LpMetadata> local_metadata;
     if (!metadata) {
-        if (!params.metadata_slot) {
+        if (!metadata_slot) {
             LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
             return false;
         }
-        auto slot = *params.metadata_slot;
-        if (local_metadata = ReadMetadata(params.block_device, slot); !local_metadata) {
-            LOG(ERROR) << "Could not read partition table for: " << params.block_device;
+        auto slot = *metadata_slot;
+        if (owned->metadata = ReadMetadata(block_device, slot); !owned->metadata) {
+            LOG(ERROR) << "Could not read partition table for: " << block_device;
             return false;
         }
-        metadata = local_metadata.get();
+        metadata = owned->metadata.get();
     }
 
     // Find the partition by name if needed.
-    const LpMetadataPartition* partition = params.partition;
     if (!partition) {
-        for (const auto& iter : metadata->partitions) {
-            if (GetPartitionName(iter) == params.partition_name) {
-                partition = &iter;
+        for (const auto& metadata_partition : metadata->partitions) {
+            if (android::fs_mgr::GetPartitionName(metadata_partition) == partition_name) {
+                partition = &metadata_partition;
                 break;
             }
         }
-        if (!partition) {
-            LERROR << "Could not find any partition with name: " << params.partition_name;
-            return false;
-        }
+    }
+    if (!partition) {
+        LERROR << "Could not find any partition with name: " << partition_name;
+        return false;
+    }
+    if (partition_name.empty()) {
+        partition_name = android::fs_mgr::GetPartitionName(*partition);
+    } else if (partition_name != android::fs_mgr::GetPartitionName(*partition)) {
+        LERROR << "Inconsistent partition_name " << partition_name << " with partition "
+               << android::fs_mgr::GetPartitionName(*partition);
+        return false;
     }
 
-    PartitionOpener default_opener;
-    const IPartitionOpener* opener =
-            params.partition_opener ? params.partition_opener : &default_opener;
+    if (!partition_opener) {
+        owned->partition_opener = std::make_unique<PartitionOpener>();
+        partition_opener = owned->partition_opener.get();
+    }
+
+    if (device_name.empty()) {
+        device_name = partition_name;
+    }
+
+    return true;
+}
+
+bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {
+    CreateLogicalPartitionParams::OwnedData owned_data;
+    if (!params.InitDefaults(&owned_data)) return false;
 
     DmTable table;
-    if (!CreateDmTable(*opener, *metadata, *partition, params.block_device, &table)) {
+    if (!CreateDmTable(*params.partition_opener, *params.metadata, *params.partition,
+                       params.block_device, &table)) {
         return false;
     }
     if (params.force_writable) {
         table.set_readonly(false);
     }
 
-    std::string device_name = params.device_name;
-    if (device_name.empty()) {
-        device_name = GetPartitionName(*partition);
-    }
-
     DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.CreateDevice(device_name, table, path, params.timeout_ms)) {
+    if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
         return false;
     }
-    LINFO << "Created logical partition " << device_name << " on device " << *path;
+    LINFO << "Created logical partition " << params.device_name << " on device " << *path;
     return true;
 }
 
+std::string CreateLogicalPartitionParams::GetDeviceName() const {
+    if (!device_name.empty()) return device_name;
+    return GetPartitionName();
+}
+
+std::string CreateLogicalPartitionParams::GetPartitionName() const {
+    if (!partition_name.empty()) return partition_name;
+    if (partition) return android::fs_mgr::GetPartitionName(*partition);
+    return "<unknown partition>";
+}
+
 bool UnmapDevice(const std::string& name) {
     DeviceMapper& dm = DeviceMapper::Instance();
     if (!dm.DeleteDevice(name)) {
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 8e2fdbb..a912208 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -77,14 +77,38 @@
     // If non-null, this will use the specified IPartitionOpener rather than
     // the default one.
     const IPartitionOpener* partition_opener = nullptr;
+
+    // Helpers for determining the effective partition and device name.
+    std::string GetPartitionName() const;
+    std::string GetDeviceName() const;
+
+    // Specify ownership of fields. The ownership of these fields are managed
+    // by the caller of InitDefaults().
+    // These are not declared in CreateLogicalPartitionParams so that the
+    // copy constructor is not deleted.
+    struct OwnedData {
+        std::unique_ptr<LpMetadata> metadata;
+        std::unique_ptr<IPartitionOpener> partition_opener;
+    };
+
+    // Fill in default values for |params| that CreateLogicalPartition assumes. Caller does
+    // not need to call this before calling CreateLogicalPartition; CreateLogicalPartition sets
+    // values when they are missing.
+    // Caller is responsible for destroying owned_data when |this| is not used.
+    bool InitDefaults(OwnedData* owned);
 };
 
-bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
+bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path);
 
 // Destroy the block device for a logical partition, by name. If |timeout_ms|
 // is non-zero, then this will block until the device path has been unlinked.
 bool DestroyLogicalPartition(const std::string& name);
 
+// Helper for populating a DmTable for a logical partition.
+bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
+                   const LpMetadataPartition& partition, const std::string& super_device,
+                   android::dm::DmTable* table);
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 7c9804c..65f6e12 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -244,7 +244,8 @@
 }
 
 std::string DmTargetDefaultKey::GetParameterString() const {
-    return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_);
+    return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_) +
+           (set_dun_ ? " 1 set_dun" : "");
 }
 
 }  // namespace dm
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index a66ab7a..a78bc71 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -280,12 +280,14 @@
 class DmTargetDefaultKey final : public DmTarget {
   public:
     DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
-                       const std::string& key, const std::string& blockdev, uint64_t start_sector)
+                       const std::string& key, const std::string& blockdev, uint64_t start_sector,
+                       bool set_dun = false)
         : DmTarget(start, length),
           cipher_(cipher),
           key_(key),
           blockdev_(blockdev),
-          start_sector_(start_sector) {}
+          start_sector_(start_sector),
+          set_dun_(set_dun) {}
 
     std::string name() const override { return "default-key"; }
     bool Valid() const override { return true; }
@@ -296,6 +298,7 @@
     std::string key_;
     std::string blockdev_;
     uint64_t start_sector_;
+    bool set_dun_;
 };
 
 }  // namespace dm
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 5b377ae..f0142bb 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -55,13 +55,14 @@
     export_include_dirs: ["include"],
 }
 
-cc_test {
-    name: "liblp_test_static",
+cc_defaults {
+    name: "liblp_test_defaults",
     defaults: ["fs_mgr_defaults"],
     cppflags: [
         "-Wno-unused-parameter",
     ],
     static_libs: [
+        "libcutils",
         "libgmock",
         "libfs_mgr",
         "liblp",
@@ -69,15 +70,25 @@
     stl: "libc++_static",
     srcs: [
         "builder_test.cpp",
+        "device_test.cpp",
         "io_test.cpp",
         "test_partition_opener.cpp",
         "utility_test.cpp",
     ],
-    target: {
-        android: {
-            static_libs: [
-                "libcutils",
-            ],
-        },
-    },
 }
+
+cc_test {
+    name: "liblp_test",
+    defaults: ["liblp_test_defaults"],
+    test_config: "liblp_test.xml",
+    test_suites: [
+        "device-tests",
+        "vts-core",
+    ],
+}
+
+cc_test {
+    name: "vts_kernel_liblp_test",
+    defaults: ["liblp_test_defaults"],
+}
+
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
index fe1002c..2eb0ad1 100644
--- a/fs_mgr/liblp/AndroidTest.xml
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -21,8 +21,8 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
       <option name="test-module-name" value="VtsKernelLiblpTest"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/liblp_test_static/liblp_test_static" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/liblp_test_static/liblp_test_static" />
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_kernel_liblp_test/vts_kernel_liblp_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_kernel_liblp_test/vts_kernel_liblp_test" />
         <option name="binary-test-type" value="gtest"/>
         <option name="test-timeout" value="1m"/>
         <option name="precondition-first-api-level" value="29" />
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
new file mode 100644
index 0000000..04bcbda
--- /dev/null
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "liblp_test"
+    }
+  ]
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 77c9c28..c91fbe4 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -45,7 +45,7 @@
     return true;
 }
 
-Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
     : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
 
 void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
@@ -232,7 +232,7 @@
     memset(&header_, 0, sizeof(header_));
     header_.magic = LP_METADATA_HEADER_MAGIC;
     header_.major_version = LP_METADATA_MAJOR_VERSION;
-    header_.minor_version = LP_METADATA_MINOR_VERSION;
+    header_.minor_version = LP_METADATA_MINOR_VERSION_MIN;
     header_.header_size = sizeof(header_);
     header_.partitions.entry_size = sizeof(LpMetadataPartition);
     header_.extents.entry_size = sizeof(LpMetadataExtent);
@@ -436,10 +436,10 @@
 }
 
 Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
-    return AddPartition(name, std::string(kDefaultGroup), attributes);
+    return AddPartition(name, kDefaultGroup, attributes);
 }
 
-Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
+Partition* MetadataBuilder::AddPartition(std::string_view name, std::string_view group_name,
                                          uint32_t attributes) {
     if (name.empty()) {
         LERROR << "Partition must have a non-empty name.";
@@ -457,7 +457,7 @@
     return partitions_.back().get();
 }
 
-Partition* MetadataBuilder::FindPartition(const std::string& name) {
+Partition* MetadataBuilder::FindPartition(std::string_view name) {
     for (const auto& partition : partitions_) {
         if (partition->name() == name) {
             return partition.get();
@@ -796,6 +796,11 @@
             return nullptr;
         }
 
+        if (partition->attributes() & LP_PARTITION_ATTR_UPDATED) {
+            static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
+            metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
+        }
+
         strncpy(part.name, partition->name().c_str(), sizeof(part.name));
         part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
         part.num_extents = static_cast<uint32_t>(partition->extents().size());
@@ -953,7 +958,7 @@
     return names;
 }
 
-void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
+void MetadataBuilder::RemoveGroupAndPartitions(std::string_view group_name) {
     if (group_name == kDefaultGroup) {
         // Cannot remove the default group.
         return;
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index b3c13e6..c5b4047 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,34 +14,25 @@
  * limitations under the License.
  */
 
-#include <fs_mgr.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
 
-#include "mock_property_fetcher.h"
+#include "liblp_test.h"
 #include "utility.h"
 
 using namespace std;
 using namespace android::fs_mgr;
-using ::android::fs_mgr::MockPropertyFetcher;
+using namespace android::fs_mgr::testing;
 using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::ElementsAre;
 using ::testing::NiceMock;
 using ::testing::Return;
 
-static void ResetPropertyFetcher() {
-    IPropertyFetcher::OverrideForTesting(std::make_unique<NiceMock<MockPropertyFetcher>>());
-}
-
-MockPropertyFetcher* GetMockedPropertyFetcher() {
-    return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
-}
-
 class Environment : public ::testing::Environment {
   public:
-    void SetUp() override { ResetPropertyFetcher(); }
+    void SetUp() override { ResetMockPropertyFetcher(); }
 };
 
 int main(int argc, char** argv) {
@@ -49,11 +40,7 @@
     return RUN_ALL_TESTS();
 }
 
-class BuilderTest : public ::testing::Test {
-  public:
-    void SetUp() override { ResetPropertyFetcher(); }
-    void TearDown() override { ResetPropertyFetcher(); }
-};
+class BuilderTest : public LiblpTest {};
 
 TEST_F(BuilderTest, BuildBasic) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
@@ -362,7 +349,7 @@
     const LpMetadataHeader& header = exported->header;
     EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
     EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
-    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION_MIN);
 
     ASSERT_EQ(exported->partitions.size(), 2);
     ASSERT_EQ(exported->extents.size(), 3);
@@ -460,23 +447,6 @@
     EXPECT_EQ(builder, nullptr);
 }
 
-TEST_F(BuilderTest, block_device_info) {
-    PartitionOpener opener;
-
-    BlockDeviceInfo device_info;
-    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
-
-    // Sanity check that the device doesn't give us some weird inefficient
-    // alignment.
-    ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
-    ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
-    ASSERT_LE(device_info.alignment_offset, INT_MAX);
-    ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
-
-    // Having an alignment offset > alignment doesn't really make sense.
-    ASSERT_LT(device_info.alignment_offset, device_info.alignment);
-}
-
 TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
     BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
new file mode 100644
index 0000000..382d53d
--- /dev/null
+++ b/fs_mgr/liblp/device_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 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 <android-base/properties.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+#include <liblp/liblp.h>
+#include <liblp/metadata_format.h>
+#include <liblp/partition_opener.h>
+#include <liblp/property_fetcher.h>
+
+#include "liblp_test.h"
+
+using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
+using ::testing::Return;
+
+// Compliance test on the actual device with dynamic partitions.
+class DeviceTest : public LiblpTest {
+  public:
+    void SetUp() override {
+        // Read real properties.
+        IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());
+        if (!IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions",
+                                                              false)) {
+            GTEST_SKIP() << "Device doesn't have dynamic partitions enabled, skipping";
+        }
+    }
+};
+
+TEST_F(DeviceTest, BlockDeviceInfo) {
+    PartitionOpener opener;
+    BlockDeviceInfo device_info;
+    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
+
+    // Sanity check that the device doesn't give us some weird inefficient
+    // alignment.
+    EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+    EXPECT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+    EXPECT_LE(device_info.alignment_offset, INT_MAX);
+    EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
+
+    // Having an alignment offset > alignment doesn't really make sense.
+    EXPECT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
+    auto slot_suffix = fs_mgr_get_slot_suffix();
+    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto super_name = fs_mgr_get_super_partition_name(slot_number);
+    auto metadata = ReadMetadata(super_name, slot_number);
+    EXPECT_NE(metadata, nullptr);
+}
+
+TEST_F(DeviceTest, ReadSuperPartitionOtherSlot) {
+    auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+    if (other_slot_suffix.empty()) {
+        GTEST_SKIP() << "No other slot, skipping";
+    }
+    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions_retrofit",
+                                                         false)) {
+        GTEST_SKIP() << "Device with retrofit dynamic partition may not have metadata at other "
+                     << "slot, skipping";
+    }
+
+    auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+    auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+    auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+    EXPECT_NE(other_metadata, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index c3f6e91..e3b591a 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -108,7 +108,7 @@
     friend class MetadataBuilder;
 
   public:
-    Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
+    Partition(std::string_view name, std::string_view group_name, uint32_t attributes);
 
     // Add a raw extent.
     void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -123,6 +123,7 @@
     const std::string& name() const { return name_; }
     const std::string& group_name() const { return group_name_; }
     uint32_t attributes() const { return attributes_; }
+    void set_attributes(uint32_t attributes) { attributes_ = attributes; }
     const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
     uint64_t size() const { return size_; }
 
@@ -213,7 +214,7 @@
 
     // Add a partition, returning a handle so it can be sized as needed. If a
     // partition with the given name already exists, nullptr is returned.
-    Partition* AddPartition(const std::string& name, const std::string& group_name,
+    Partition* AddPartition(std::string_view name, std::string_view group_name,
                             uint32_t attributes);
 
     // Same as AddPartition above, but uses the default partition group which
@@ -224,7 +225,7 @@
     void RemovePartition(const std::string& name);
 
     // Find a partition by name. If no partition is found, nullptr is returned.
-    Partition* FindPartition(const std::string& name);
+    Partition* FindPartition(std::string_view name);
 
     // Find a group by name. If no group is found, nullptr is returned.
     PartitionGroup* FindGroup(std::string_view name);
@@ -268,7 +269,7 @@
     std::vector<std::string> ListGroups() const;
 
     // Remove all partitions belonging to a group, then remove the group.
-    void RemoveGroupAndPartitions(const std::string& group_name);
+    void RemoveGroupAndPartitions(std::string_view group_name);
 
     // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
     void SetAutoSlotSuffixing();
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 8934aaf..6e928b4 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -39,7 +39,11 @@
 
 /* Current metadata version. */
 #define LP_METADATA_MAJOR_VERSION 10
-#define LP_METADATA_MINOR_VERSION 0
+#define LP_METADATA_MINOR_VERSION_MIN 0
+#define LP_METADATA_MINOR_VERSION_MAX 1
+
+/* Metadata version needed to use the UPDATED partition attribute. */
+#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1
 
 /* Attributes for the LpMetadataPartition::attributes field.
  *
@@ -58,8 +62,20 @@
  */
 #define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
 
-/* Mask that defines all valid attributes. */
-#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
+ * It signals that the partition was created (or modified) for a snapshot-based
+ * update. If this flag is not present, the partition was likely flashed via
+ * fastboot.
+ */
+#define LP_PARTITION_ATTR_UPDATED (1 << 2)
+
+/* Mask that defines all valid attributes. When changing this, make sure to
+ * update ParseMetadata().
+ */
+#define LP_PARTITION_ATTRIBUTE_MASK_V0 \
+    (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTR_UPDATED)
+#define LP_PARTITION_ATTRIBUTE_MASK LP_PARTITION_ATTRIBUTE_MASK_V1
 
 /* Default name of the physical partition that holds logical partition entries.
  * The layout of this partition will look like:
diff --git a/fs_mgr/liblp/mock_property_fetcher.h b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
similarity index 79%
rename from fs_mgr/liblp/mock_property_fetcher.h
rename to fs_mgr/liblp/include/liblp/mock_property_fetcher.h
index 0c28710..f15611b 100644
--- a/fs_mgr/liblp/mock_property_fetcher.h
+++ b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
@@ -22,6 +22,7 @@
 
 namespace android {
 namespace fs_mgr {
+namespace testing {
 
 class MockPropertyFetcher : public IPropertyFetcher {
   public:
@@ -41,7 +42,15 @@
     }
 };
 
+static inline void ResetMockPropertyFetcher() {
+    IPropertyFetcher::OverrideForTesting(
+            std::make_unique<::testing::NiceMock<MockPropertyFetcher>>());
+}
+
+static inline MockPropertyFetcher* GetMockedPropertyFetcher() {
+    return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
+}
+
+}  // namespace testing
 }  // namespace fs_mgr
 }  // namespace android
-
-android::fs_mgr::MockPropertyFetcher* GetMockedPropertyFetcher();
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 3dace25..22f6746 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,14 +21,12 @@
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
-#include <fs_mgr.h>
-#include <fstab/fstab.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
 
 #include "images.h"
-#include "mock_property_fetcher.h"
+#include "liblp_test.h"
 #include "reader.h"
 #include "test_partition_opener.h"
 #include "utility.h"
@@ -36,6 +34,7 @@
 
 using namespace std;
 using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
 using ::testing::_;
 using ::testing::Return;
 using unique_fd = android::base::unique_fd;
@@ -124,7 +123,7 @@
 }
 
 // Test that our CreateFakeDisk() function works.
-TEST(liblp, CreateFakeDisk) {
+TEST_F(LiblpTest, CreateFakeDisk) {
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
@@ -140,7 +139,7 @@
 
 // Flashing metadata should not work if the metadata was created for a larger
 // disk than the destination disk.
-TEST(liblp, ExportDiskTooSmall) {
+TEST_F(LiblpTest, ExportDiskTooSmall) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
@@ -157,7 +156,7 @@
 }
 
 // Test the basics of flashing a partition and reading it back.
-TEST(liblp, FlashAndReadback) {
+TEST_F(LiblpTest, FlashAndReadback) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -207,7 +206,7 @@
 }
 
 // Test that we can update metadata slots without disturbing others.
-TEST(liblp, UpdateAnyMetadataSlot) {
+TEST_F(LiblpTest, UpdateAnyMetadataSlot) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -252,7 +251,7 @@
     }
 }
 
-TEST(liblp, InvalidMetadataSlot) {
+TEST_F(LiblpTest, InvalidMetadataSlot) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -271,7 +270,7 @@
 
 // Test that updating a metadata slot does not allow it to be computed based
 // on mismatching geometry.
-TEST(liblp, NoChangingGeometry) {
+TEST_F(LiblpTest, NoChangingGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -300,7 +299,7 @@
 }
 
 // Test that changing one bit of metadata is enough to break the checksum.
-TEST(liblp, BitFlipGeometry) {
+TEST_F(LiblpTest, BitFlipGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -319,7 +318,7 @@
     EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
 }
 
-TEST(liblp, ReadBackupGeometry) {
+TEST_F(LiblpTest, ReadBackupGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -339,7 +338,7 @@
     EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
 }
 
-TEST(liblp, ReadBackupMetadata) {
+TEST_F(LiblpTest, ReadBackupMetadata) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -366,7 +365,7 @@
 
 // Test that we don't attempt to write metadata if it would overflow its
 // reserved space.
-TEST(liblp, TooManyPartitions) {
+TEST_F(LiblpTest, TooManyPartitions) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
 
@@ -420,7 +419,7 @@
 }
 
 // Test that we can read and write image files.
-TEST(liblp, ImageFiles) {
+TEST_F(LiblpTest, ImageFiles) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -436,7 +435,7 @@
 }
 
 // Test that we can read images from buffers.
-TEST(liblp, ImageFilesInMemory) {
+TEST_F(LiblpTest, ImageFilesInMemory) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -496,7 +495,7 @@
 
 // Test that an interrupted flash operation on the "primary" copy of metadata
 // is not fatal.
-TEST(liblp, UpdatePrimaryMetadataFailure) {
+TEST_F(LiblpTest, UpdatePrimaryMetadataFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -524,7 +523,7 @@
 
 // Test that an interrupted flash operation on the "backup" copy of metadata
 // is not fatal.
-TEST(liblp, UpdateBackupMetadataFailure) {
+TEST_F(LiblpTest, UpdateBackupMetadataFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -553,7 +552,7 @@
 // Test that an interrupted write *in between* writing metadata will read
 // the correct metadata copy. The primary is always considered newer than
 // the backup.
-TEST(liblp, UpdateMetadataCleanFailure) {
+TEST_F(LiblpTest, UpdateMetadataCleanFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -590,7 +589,7 @@
 }
 
 // Test that writing a sparse image can be read back.
-TEST(liblp, FlashSparseImage) {
+TEST_F(LiblpTest, FlashSparseImage) {
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
@@ -624,7 +623,7 @@
     ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
 }
 
-TEST(liblp, AutoSlotSuffixing) {
+TEST_F(LiblpTest, AutoSlotSuffixing) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -667,7 +666,7 @@
     EXPECT_EQ(metadata->groups[1].flags, 0);
 }
 
-TEST(liblp, UpdateRetrofit) {
+TEST_F(LiblpTest, UpdateRetrofit) {
     ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
             .WillByDefault(Return(true));
 
@@ -699,7 +698,7 @@
     ASSERT_TRUE(updated->extents.empty());
 }
 
-TEST(liblp, UpdateNonRetrofit) {
+TEST_F(LiblpTest, UpdateNonRetrofit) {
     ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
             .WillByDefault(Return(false));
 
@@ -715,18 +714,31 @@
     EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
 }
 
-TEST(liblp, ReadSuperPartition) {
-    auto slot_suffix = fs_mgr_get_slot_suffix();
-    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
-    auto super_name = fs_mgr_get_super_partition_name(slot_number);
-    auto metadata = ReadMetadata(super_name, slot_number);
-    ASSERT_NE(metadata, nullptr);
+TEST_F(LiblpTest, UpdateVirtualAB) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+            .WillByDefault(Return(true));
 
-    if (!slot_suffix.empty()) {
-        auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
-        auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
-        auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
-        auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
-        ASSERT_NE(other_metadata, nullptr);
-    }
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+    auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+    ASSERT_NE(builder, nullptr);
+    auto updated = builder->Export();
+    ASSERT_NE(updated, nullptr);
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *updated.get(), 1));
+
+    // Validate old slot.
+    auto metadata = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(metadata, nullptr);
+    ASSERT_EQ(metadata->header.minor_version, 0);
+    ASSERT_GE(metadata->partitions.size(), 1);
+    ASSERT_EQ(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
+
+    // Validate new slot.
+    metadata = ReadMetadata(opener, "super", 1);
+    ASSERT_NE(metadata, nullptr);
+    ASSERT_EQ(metadata->header.minor_version, 1);
+    ASSERT_GE(metadata->partitions.size(), 1);
+    ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
 }
diff --git a/fs_mgr/liblp/liblp_test.h b/fs_mgr/liblp/liblp_test.h
new file mode 100644
index 0000000..c61aaa5
--- /dev/null
+++ b/fs_mgr/liblp/liblp_test.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 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 <gtest/gtest.h>
+
+#include <liblp/mock_property_fetcher.h>
+
+namespace android {
+namespace fs_mgr {
+namespace testing {
+
+class LiblpTest : public ::testing::Test {
+  public:
+    void SetUp() override { ResetMockPropertyFetcher(); }
+    void TearDown() override { ResetMockPropertyFetcher(); }
+};
+
+}  // namespace testing
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
new file mode 100644
index 0000000..d9ee12e
--- /dev/null
+++ b/fs_mgr/liblp/liblp_test.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for liblp_test">
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="vts-core" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="liblp_test" />
+    </test>
+</configuration>
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index f1e8fc2..1d4db85 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -41,7 +41,21 @@
     if (android::base::StartsWith(path, "/")) {
         return path;
     }
-    return "/dev/block/by-name/" + path;
+
+    auto by_name = "/dev/block/by-name/" + path;
+    if (access(by_name.c_str(), F_OK) != 0) {
+        // If the by-name symlink doesn't exist, as a special case we allow
+        // certain devices to be used as partition names. This can happen if a
+        // Dynamic System Update is installed to an sdcard, which won't be in
+        // the boot device list.
+        //
+        // We whitelist because most devices in /dev/block are not valid for
+        // storing fiemaps.
+        if (android::base::StartsWith(path, "mmcblk")) {
+            return "/dev/block/" + path;
+        }
+    }
+    return by_name;
 }
 
 bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 8dbe955..aecf685 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -181,7 +181,7 @@
     }
     // Check that the version is compatible.
     if (header.major_version != LP_METADATA_MAJOR_VERSION ||
-        header.minor_version > LP_METADATA_MINOR_VERSION) {
+        header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
         LERROR << "Logical partition metadata has incompatible version.";
         return false;
     }
@@ -245,6 +245,13 @@
         return nullptr;
     }
 
+    uint32_t valid_attributes = 0;
+    if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
+        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V1;
+    } else {
+        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
+    }
+
     // ValidateTableSize ensured that |cursor| is valid for the number of
     // entries in the table.
     uint8_t* cursor = buffer.get() + header.partitions.offset;
@@ -253,7 +260,7 @@
         memcpy(&partition, cursor, sizeof(partition));
         cursor += header.partitions.entry_size;
 
-        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+        if (partition.attributes & ~valid_attributes) {
             LERROR << "Logical partition has invalid attribute set.";
             return nullptr;
         }
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 17f05aa..afcce8f 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -269,6 +269,7 @@
                   << "; this partition should not belong to this group!";
             continue;  // not adding to new_partition_ptrs
         }
+        partition.attributes |= LP_PARTITION_ATTR_UPDATED;
         partition.group_index = std::distance(new_group_ptrs.begin(), it);
         new_partition_ptrs.push_back(&partition);
     }
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 128a4f9..a54db58 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -28,6 +28,8 @@
     ],
     static_libs: [
         "libdm",
+        "libfs_mgr",
+        "liblp",
     ],
     whole_static_libs: [
         "libext2_uuid",
@@ -74,6 +76,7 @@
     defaults: ["libsnapshot_defaults"],
     srcs: [
         "snapshot_test.cpp",
+        "test_helpers.cpp",
     ],
     shared_libs: [
         "libbinder",
@@ -83,6 +86,7 @@
         "libcutils",
         "libcrypto",
         "libfs_mgr",
+        "libgmock",
         "liblp",
         "libsnapshot",
     ],
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 1630ee5..e3fc4f6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -24,6 +24,7 @@
 #include <android-base/unique_fd.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -37,6 +38,11 @@
 class IImageManager;
 }  // namespace fiemap
 
+namespace fs_mgr {
+struct CreateLogicalPartitionParams;
+class IPartitionOpener;
+}  // namespace fs_mgr
+
 namespace snapshot {
 
 enum class UpdateState : unsigned int {
@@ -60,10 +66,18 @@
     MergeCompleted,
 
     // Merging failed due to an unrecoverable error.
-    MergeFailed
+    MergeFailed,
+
+    // The update was implicitly cancelled, either by a rollback or a flash
+    // operation via fastboot. This state can only be returned by WaitForMerge.
+    Cancelled
 };
 
 class SnapshotManager final {
+    using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
+    using LpMetadata = android::fs_mgr::LpMetadata;
+    using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+
   public:
     // Dependency injection for testing.
     class IDeviceInfo {
@@ -72,6 +86,8 @@
         virtual std::string GetGsidDir() const = 0;
         virtual std::string GetMetadataDir() const = 0;
         virtual std::string GetSlotSuffix() const = 0;
+        virtual std::string GetSuperDevice(uint32_t slot) const = 0;
+        virtual const IPartitionOpener& GetPartitionOpener() const = 0;
     };
 
     ~SnapshotManager();
@@ -81,6 +97,14 @@
     // instance will be created.
     static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
 
+    // This is similar to New(), except designed specifically for first-stage
+    // init.
+    static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
+
+    // Helper function for first-stage init to check whether a SnapshotManager
+    // might be needed to perform first-stage mounts.
+    static bool IsSnapshotManagerNeeded();
+
     // Begin an update. This must be called before creating any snapshots. It
     // will fail if GetUpdateState() != None.
     bool BeginUpdate();
@@ -98,12 +122,15 @@
     // update has been marked successful after booting.
     bool InitiateMerge();
 
-    // Wait for the current merge to finish, then perform cleanup when it
-    // completes. It is necessary to call this after InitiateMerge(), or when
-    // a merge state is detected during boot.
+    // Perform any necessary post-boot actions. This should be run soon after
+    // /data is mounted.
     //
-    // Note that after calling WaitForMerge(), GetUpdateState() may still return
-    // that a merge is in progress:
+    // If a merge is in progress, this function will block until the merge is
+    // completed. If a merge or update was cancelled, this will clean up any
+    // update artifacts and return.
+    //
+    // Note that after calling this, GetUpdateState() may still return that a
+    // merge is in progress:
     //   MergeFailed indicates that a fatal error occurred. WaitForMerge() may
     //   called any number of times again to attempt to make more progress, but
     //   we do not expect it to succeed if a catastrophic error occurred.
@@ -116,7 +143,7 @@
     //
     //   MergeCompleted indicates that the update has fully completed.
     //   GetUpdateState will return None, and a new update can begin.
-    UpdateState WaitForMerge();
+    UpdateState ProcessUpdateState();
 
     // Find the status of the current update, if any.
     //
@@ -126,13 +153,26 @@
     //   Other: 0
     UpdateState GetUpdateState(double* progress = nullptr);
 
+    // If this returns true, first-stage mount must call
+    // CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
+    bool NeedSnapshotsInFirstStageMount();
+
+    // Perform first-stage mapping of snapshot targets. This replaces init's
+    // call to CreateLogicalPartitions when snapshots are present.
+    bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
+
   private:
+    FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
-    FRIEND_TEST(SnapshotTest, MapSnapshot);
+    FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
+    FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
+    FRIEND_TEST(SnapshotTest, FlashSuperDuringMerge);
+    FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);
     FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
-    FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
+    FRIEND_TEST(SnapshotTest, MapSnapshot);
     FRIEND_TEST(SnapshotTest, Merge);
     FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
+    FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     friend class SnapshotTest;
 
     using DmTargetSnapshot = android::dm::DmTargetSnapshot;
@@ -141,9 +181,12 @@
 
     explicit SnapshotManager(IDeviceInfo* info);
 
-    // This is created lazily since it connects via binder.
+    // This is created lazily since it can connect via binder.
     bool EnsureImageManager();
 
+    // Helper for first-stage init.
+    bool ForceLocalImageManager();
+
     // Helper function for tests.
     IImageManager* image_manager() const { return images_.get(); }
 
@@ -211,6 +254,14 @@
     // List the known snapshot names.
     bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
 
+    // Check for a cancelled or rolled back merge, returning true if such a
+    // condition was detected and handled.
+    bool HandleCancelledUpdate(LockedFile* lock);
+
+    // Remove artifacts created by the update process, such as snapshots, and
+    // set the update state to None.
+    bool RemoveAllUpdateState(LockedFile* lock);
+
     // Interact with /metadata/ota/state.
     std::unique_ptr<LockedFile> OpenStateFile(int open_flags, int lock_flags);
     std::unique_ptr<LockedFile> LockShared();
@@ -238,6 +289,7 @@
     bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
     void AcknowledgeMergeSuccess(LockedFile* lock);
     void AcknowledgeMergeFailure();
+    bool IsCancelledSnapshot(const std::string& snapshot_name);
 
     // Note that these require the name of the device containing the snapshot,
     // which may be the "inner" device. Use GetsnapshotDeviecName().
@@ -278,6 +330,7 @@
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
     std::unique_ptr<IImageManager> images_;
+    bool has_local_image_manager_ = false;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index fef5c06..71457ee 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -20,6 +20,7 @@
 #include <sys/unistd.h>
 
 #include <thread>
+#include <unordered_set>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -27,9 +28,12 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
 
 namespace android {
 namespace snapshot {
@@ -43,22 +47,34 @@
 using android::dm::kSectorSize;
 using android::dm::SnapshotStorageMode;
 using android::fiemap::IImageManager;
+using android::fs_mgr::CreateDmTable;
+using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::GetPartitionName;
+using android::fs_mgr::LpMetadata;
+using android::fs_mgr::SlotNumberForSlotSuffix;
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
 // Unit is sectors, this is a 4K chunk.
 static constexpr uint32_t kSnapshotChunkSize = 8;
-
-static constexpr char kSnapshotBootIndicatorFile[] = "snapshot-boot";
+static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
 
 class DeviceInfo final : public SnapshotManager::IDeviceInfo {
   public:
     std::string GetGsidDir() const override { return "ota"s; }
     std::string GetMetadataDir() const override { return "/metadata/ota"s; }
     std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
+    std::string GetSuperDevice(uint32_t slot) const override {
+        return fs_mgr_get_super_partition_name(slot);
+    }
+
+  private:
+    android::fs_mgr::PartitionOpener opener_;
 };
 
-// Note: IIMageManager is an incomplete type in the header, so the default
+// Note: IImageManager is an incomplete type in the header, so the default
 // destructor doesn't work.
 SnapshotManager::~SnapshotManager() {}
 
@@ -69,6 +85,14 @@
     return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
 }
 
+std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
+    auto sm = New(info);
+    if (!sm || !sm->ForceLocalImageManager()) {
+        return nullptr;
+    }
+    return sm;
+}
+
 SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
     gsid_dir_ = device_->GetGsidDir();
     metadata_dir_ = device_->GetMetadataDir();
@@ -78,6 +102,10 @@
     return snapshot_name + "-cow";
 }
 
+static std::string GetBaseDeviceName(const std::string& partition_name) {
+    return partition_name + "-base";
+}
+
 bool SnapshotManager::BeginUpdate() {
     auto file = LockExclusive();
     if (!file) return false;
@@ -100,17 +128,20 @@
         LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
         return false;
     }
+    return RemoveAllUpdateState(file.get());
+}
 
-    if (!RemoveAllSnapshots(file.get())) {
+bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
+    if (!RemoveAllSnapshots(lock)) {
         LOG(ERROR) << "Could not remove all snapshots";
         return false;
     }
 
-    if (!WriteUpdateState(file.get(), UpdateState::None)) {
-        LOG(ERROR) << "Could not write new update state";
-        return false;
-    }
-    return true;
+    RemoveSnapshotBootIndicator();
+
+    // If this fails, we'll keep trying to remove the update state (as the
+    // device reboots or starts a new update) until it finally succeeds.
+    return WriteUpdateState(lock, UpdateState::None);
 }
 
 bool SnapshotManager::FinishedSnapshotWrites() {
@@ -197,8 +228,8 @@
     }
 
     // Validate the block device size, as well as the requested snapshot size.
-    // During this we also compute the linear sector region if any.
-    {
+    // Note that during first-stage init, we don't have the device paths.
+    if (android::base::StartsWith(base_device, "/")) {
         unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
         if (fd < 0) {
             PLOG(ERROR) << "open failed: " << base_device;
@@ -228,8 +259,17 @@
 
     auto cow_name = GetCowName(name);
 
+    bool ok;
     std::string cow_dev;
-    if (!images_->MapImageDevice(cow_name, timeout_ms, &cow_dev)) {
+    if (has_local_image_manager_) {
+        // If we forced a local image manager, it means we don't have binder,
+        // which means first-stage init. We must use device-mapper.
+        const auto& opener = device_->GetPartitionOpener();
+        ok = images_->MapImageWithDeviceMapper(opener, cow_name, &cow_dev);
+    } else {
+        ok = images_->MapImageDevice(cow_name, timeout_ms, &cow_dev);
+    }
+    if (!ok) {
         LOG(ERROR) << "Could not map image device: " << cow_name;
         return false;
     }
@@ -305,7 +345,7 @@
     }
 
     auto& dm = DeviceMapper::Instance();
-    if (dm.GetState(name) != DmDeviceState::INVALID && !dm.DeleteDevice(name)) {
+    if (!dm.DeleteDeviceIfExists(name)) {
         LOG(ERROR) << "Could not delete snapshot device: " << name;
         return false;
     }
@@ -313,8 +353,7 @@
     // There may be an extra device, since the kernel doesn't let us have a
     // snapshot and linear target in the same table.
     auto dm_name = GetSnapshotDeviceName(name, status);
-    if (name != dm_name && dm.GetState(dm_name) != DmDeviceState::INVALID &&
-        !dm.DeleteDevice(dm_name)) {
+    if (name != dm_name && !dm.DeleteDeviceIfExists(dm_name)) {
         LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
         return false;
     }
@@ -331,14 +370,13 @@
     if (!EnsureImageManager()) return false;
 
     auto cow_name = GetCowName(name);
-    if (!images_->BackingImageExists(cow_name)) {
-        return true;
-    }
-    if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
-        return false;
-    }
-    if (!images_->DeleteBackingImage(cow_name)) {
-        return false;
+    if (images_->BackingImageExists(cow_name)) {
+        if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
+            return false;
+        }
+        if (!images_->DeleteBackingImage(cow_name)) {
+            return false;
+        }
     }
 
     std::string error;
@@ -544,9 +582,12 @@
 // Note that when a merge fails, we will *always* try again to complete the
 // merge each time the device boots. There is no harm in doing so, and if
 // the problem was transient, we might manage to get a new outcome.
-UpdateState SnapshotManager::WaitForMerge() {
+UpdateState SnapshotManager::ProcessUpdateState() {
     while (true) {
         UpdateState state = CheckMergeState();
+        if (state == UpdateState::MergeFailed) {
+            AcknowledgeMergeFailure();
+        }
         if (state != UpdateState::Merging) {
             // Either there is no merge, or the merge was finished, so no need
             // to keep waiting.
@@ -562,15 +603,16 @@
 UpdateState SnapshotManager::CheckMergeState() {
     auto lock = LockExclusive();
     if (!lock) {
-        AcknowledgeMergeFailure();
         return UpdateState::MergeFailed;
     }
 
-    auto state = CheckMergeState(lock.get());
+    UpdateState state = CheckMergeState(lock.get());
     if (state == UpdateState::MergeCompleted) {
+        // Do this inside the same lock. Failures get acknowledged without the
+        // lock, because flock() might have failed.
         AcknowledgeMergeSuccess(lock.get());
-    } else if (state == UpdateState::MergeFailed) {
-        AcknowledgeMergeFailure();
+    } else if (state == UpdateState::Cancelled) {
+        RemoveAllUpdateState(lock.get());
     }
     return state;
 }
@@ -592,10 +634,17 @@
             // run.
             break;
 
+        case UpdateState::Unverified:
+            // This is an edge case. Normally cancelled updates are detected
+            // via the merge poll below, but if we never started a merge, we
+            // need to also check here.
+            if (HandleCancelledUpdate(lock)) {
+                return UpdateState::Cancelled;
+            }
+            return state;
+
         default:
-            LOG(ERROR) << "No merge exists, cannot wait. Update state: "
-                       << static_cast<uint32_t>(state);
-            return UpdateState::None;
+            return state;
     }
 
     std::vector<std::string> snapshots;
@@ -603,6 +652,7 @@
         return UpdateState::MergeFailed;
     }
 
+    bool cancelled = false;
     bool failed = false;
     bool merging = false;
     bool needs_reboot = false;
@@ -620,6 +670,9 @@
                 break;
             case UpdateState::MergeCompleted:
                 break;
+            case UpdateState::Cancelled:
+                cancelled = true;
+                break;
             default:
                 LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
                 failed = true;
@@ -642,6 +695,14 @@
         WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
         return UpdateState::MergeNeedsReboot;
     }
+    if (cancelled) {
+        // This is an edge case, that we handle as correctly as we sensibly can.
+        // The underlying partition has changed behind update_engine, and we've
+        // removed the snapshot as a result. The exact state of the update is
+        // undefined now, but this can only happen on an unlocked device where
+        // partitions can be flashed without wiping userdata.
+        return UpdateState::Cancelled;
+    }
     return UpdateState::MergeCompleted;
 }
 
@@ -653,17 +714,30 @@
 
     std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
 
-    // During a check, we decided the merge was complete, but we were unable to
-    // collapse the device-mapper stack and perform COW cleanup. If we haven't
-    // rebooted after this check, the device will still be a snapshot-merge
-    // target. If the have rebooted, the device will now be a linear target,
-    // and we can try cleanup again.
-    if (snapshot_status.state == SnapshotState::MergeCompleted && !IsSnapshotDevice(dm_name)) {
-        // NB: It's okay if this fails now, we gave cleanup our best effort.
-        OnSnapshotMergeComplete(lock, name, snapshot_status);
-        return UpdateState::MergeCompleted;
+    if (!IsSnapshotDevice(dm_name)) {
+        if (IsCancelledSnapshot(name)) {
+            DeleteSnapshot(lock, name);
+            return UpdateState::Cancelled;
+        }
+
+        // During a check, we decided the merge was complete, but we were unable to
+        // collapse the device-mapper stack and perform COW cleanup. If we haven't
+        // rebooted after this check, the device will still be a snapshot-merge
+        // target. If the have rebooted, the device will now be a linear target,
+        // and we can try cleanup again.
+        if (snapshot_status.state == SnapshotState::MergeCompleted) {
+            // NB: It's okay if this fails now, we gave cleanup our best effort.
+            OnSnapshotMergeComplete(lock, name, snapshot_status);
+            return UpdateState::MergeCompleted;
+        }
+
+        LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
+        return UpdateState::MergeFailed;
     }
 
+    // This check is expensive so it is only enabled for debugging.
+    DCHECK(!IsCancelledSnapshot(name));
+
     std::string target_type;
     DmTargetSnapshot::Status status;
     if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
@@ -705,7 +779,7 @@
 }
 
 std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
-    return metadata_dir_ + "/" + kSnapshotBootIndicatorFile;
+    return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
 }
 
 void SnapshotManager::RemoveSnapshotBootIndicator() {
@@ -719,12 +793,7 @@
 }
 
 void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
-    RemoveSnapshotBootIndicator();
-
-    if (!WriteUpdateState(lock, UpdateState::None)) {
-        // We'll try again next reboot, ad infinitum.
-        return;
-    }
+    RemoveAllUpdateState(lock);
 }
 
 void SnapshotManager::AcknowledgeMergeFailure() {
@@ -783,25 +852,16 @@
 
 bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
                                              const SnapshotStatus& status) {
-    // Ideally, we would complete the following steps to collapse the device:
-    //  (1) Rewrite the snapshot table to be identical to the base device table.
-    //  (2) Rewrite the verity table to use the "snapshot" (now linear) device.
-    //  (3) Delete the base device.
-    //
-    // This should be possible once libsnapshot understands LpMetadata. In the
-    // meantime, we implement a simpler solution: rewriting the snapshot table
-    // to be a single dm-linear segment against the base device. While not as
-    // ideal, it still lets us remove the COW device. We can remove this
-    // implementation once the new method has been tested.
     auto& dm = DeviceMapper::Instance();
     auto dm_name = GetSnapshotDeviceName(name, status);
 
+    // Verify we have a snapshot-merge device.
     DeviceMapper::TargetInfo target;
     if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
         return false;
     }
     if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
-        // This should be impossible, it was checked above.
+        // This should be impossible, it was checked earlier.
         LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
         return false;
     }
@@ -830,7 +890,7 @@
             return false;
         }
         if (outer_table.size() != 2) {
-            LOG(ERROR) << "Expected 2 dm-linear targets for tabble " << name
+            LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
                        << ", got: " << outer_table.size();
             return false;
         }
@@ -850,31 +910,91 @@
         }
     }
 
-    // Note: we are replacing the OUTER table here, so we do not use dm_name.
-    DmTargetLinear new_target(0, num_sectors, base_device, 0);
-    LOG(INFO) << "Replacing snapshot device " << name
-              << " table with: " << new_target.GetParameterString();
+    // Grab the partition metadata for the snapshot.
+    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_device = device_->GetSuperDevice(slot);
+    const auto& opener = device_->GetPartitionOpener();
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read super partition metadata.";
+        return false;
+    }
+    auto partition = android::fs_mgr::FindPartition(*metadata.get(), name);
+    if (!partition) {
+        LOG(ERROR) << "Snapshot does not have a partition in super: " << name;
+        return false;
+    }
 
+    // Create a DmTable that is identical to the base device.
     DmTable table;
-    table.Emplace<DmTargetLinear>(new_target);
+    if (!CreateDmTable(opener, *metadata.get(), *partition, super_device, &table)) {
+        LOG(ERROR) << "Could not create a DmTable for partition: " << name;
+        return false;
+    }
+
+    // Note: we are replacing the *outer* table here, so we do not use dm_name.
     if (!dm.LoadTableAndActivate(name, table)) {
         return false;
     }
 
-    if (dm_name != name) {
-        // Attempt to delete the snapshot device. Nothing should be depending on
-        // the device, and device-mapper should have flushed remaining I/O. We
-        // could in theory replace with dm-zero (or re-use the table above), but
-        // for now it's better to know why this would fail.
-        if (!dm.DeleteDevice(dm_name)) {
-            LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
-                       << "reclaimed until after reboot.";
-            return false;
-        }
+    // Attempt to delete the snapshot device if one still exists. Nothing
+    // should be depending on the device, and device-mapper should have
+    // flushed remaining I/O. We could in theory replace with dm-zero (or
+    // re-use the table above), but for now it's better to know why this
+    // would fail.
+    if (!dm.DeleteDeviceIfExists(dm_name)) {
+        LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
+                   << "reclaimed until after reboot.";
+        return false;
+    }
+
+    // Cleanup the base device as well, since it is no longer used. This does
+    // not block cleanup.
+    auto base_name = GetBaseDeviceName(name);
+    if (!dm.DeleteDeviceIfExists(base_name)) {
+        LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
     }
     return true;
 }
 
+bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
+    std::string old_slot;
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
+        return false;
+    }
+    if (device_->GetSlotSuffix() != old_slot) {
+        // We're booted into the target slot, which means we just rebooted
+        // after applying the update.
+        return false;
+    }
+
+    // The only way we can get here is if:
+    //  (1) The device rolled back to the previous slot.
+    //  (2) This function was called prematurely before rebooting the device.
+    //  (3) fastboot set_active was used.
+    //
+    // In any case, delete the snapshots. It may be worth using the boot_control
+    // HAL to differentiate case (2).
+    RemoveAllUpdateState(lock);
+    return true;
+}
+
+bool SnapshotManager::IsCancelledSnapshot(const std::string& snapshot_name) {
+    const auto& opener = device_->GetPartitionOpener();
+    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto super_device = device_->GetSuperDevice(slot);
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
+        return false;
+    }
+    auto partition = android::fs_mgr::FindPartition(*metadata.get(), snapshot_name);
+    if (!partition) return false;
+    return (partition->attributes & LP_PARTITION_ATTR_UPDATED) == 0;
+}
+
 bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
@@ -931,6 +1051,132 @@
     return true;
 }
 
+bool SnapshotManager::IsSnapshotManagerNeeded() {
+    return access(kBootIndicatorPath, F_OK) == 0;
+}
+
+bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+    // If we fail to read, we'll wind up using CreateLogicalPartitions, which
+    // will create devices that look like the old slot, except with extra
+    // content at the end of each device. This will confuse dm-verity, and
+    // ultimately we'll fail to boot. Why not make it a fatal error and have
+    // the reason be clearer? Because the indicator file still exists, and
+    // if this was FATAL, reverting to the old slot would be broken.
+    std::string old_slot;
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
+        return false;
+    }
+    if (device_->GetSlotSuffix() == old_slot) {
+        LOG(INFO) << "Detected slot rollback, will not mount snapshots.";
+        return false;
+    }
+
+    // If we can't read the update state, it's unlikely anything else will
+    // succeed, so this is a fatal error. We'll eventually exhaust boot
+    // attempts and revert to the old slot.
+    auto lock = LockShared();
+    if (!lock) {
+        LOG(FATAL) << "Could not read update state to determine snapshot status";
+        return false;
+    }
+    switch (ReadUpdateState(lock.get())) {
+        case UpdateState::Unverified:
+        case UpdateState::Merging:
+        case UpdateState::MergeFailed:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& super_device) {
+    LOG(INFO) << "Creating logical partitions with snapshots as needed";
+
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    std::vector<std::string> snapshot_list;
+    if (!ListSnapshots(lock.get(), &snapshot_list)) {
+        return false;
+    }
+
+    std::unordered_set<std::string> live_snapshots;
+    for (const auto& snapshot : snapshot_list) {
+        SnapshotStatus status;
+        if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
+            return false;
+        }
+        if (status.state != SnapshotState::MergeCompleted) {
+            live_snapshots.emplace(snapshot);
+        }
+    }
+
+    const auto& opener = device_->GetPartitionOpener();
+    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
+        return false;
+    }
+
+    // Map logical partitions.
+    auto& dm = DeviceMapper::Instance();
+    for (const auto& partition : metadata->partitions) {
+        auto partition_name = GetPartitionName(partition);
+        if (!partition.num_extents) {
+            LOG(INFO) << "Skipping zero-length logical partition: " << partition_name;
+            continue;
+        }
+
+        if (!(partition.attributes & LP_PARTITION_ATTR_UPDATED)) {
+            LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
+                      << partition_name;
+            live_snapshots.erase(partition_name);
+        }
+
+        CreateLogicalPartitionParams params = {
+                .block_device = super_device,
+                .metadata = metadata.get(),
+                .partition = &partition,
+                .partition_opener = &opener,
+        };
+
+        if (auto iter = live_snapshots.find(partition_name); iter != live_snapshots.end()) {
+            // If the device has a snapshot, it'll need to be writable, and
+            // we'll need to create the logical partition with a marked-up name
+            // (since the snapshot will use the partition name).
+            params.force_writable = true;
+            params.device_name = GetBaseDeviceName(partition_name);
+        }
+
+        std::string ignore_path;
+        if (!CreateLogicalPartition(params, &ignore_path)) {
+            LOG(ERROR) << "Could not create logical partition " << partition_name << " as device "
+                       << params.GetDeviceName();
+            return false;
+        }
+        if (!params.force_writable) {
+            // No snapshot.
+            continue;
+        }
+
+        // We don't have ueventd in first-stage init, so use device major:minor
+        // strings instead.
+        std::string base_device;
+        if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+            LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
+            return false;
+        }
+        if (!MapSnapshot(lock.get(), partition_name, base_device, {}, &ignore_path)) {
+            LOG(ERROR) << "Could not map snapshot for partition: " << partition_name;
+            return false;
+        }
+    }
+    return true;
+}
+
 auto SnapshotManager::OpenFile(const std::string& file, int open_flags, int lock_flags)
         -> std::unique_ptr<LockedFile> {
     unique_fd fd(open(file.c_str(), open_flags | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0660));
@@ -1173,5 +1419,15 @@
     return true;
 }
 
+bool SnapshotManager::ForceLocalImageManager() {
+    images_ = android::fiemap::ImageManager::Open(gsid_dir_);
+    if (!images_) {
+        LOG(ERROR) << "Could not open ImageManager";
+        return false;
+    }
+    has_local_image_manager_ = true;
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 438ec2f..8487339 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,9 +26,14 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.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 <liblp/mock_property_fetcher.h>
+
+#include "test_helpers.h"
 
 namespace android {
 namespace snapshot {
@@ -37,38 +42,40 @@
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
 using android::fiemap::IImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::GetPartitionName;
+using android::fs_mgr::MetadataBuilder;
+using namespace ::testing;
+using namespace android::fs_mgr::testing;
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
-class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
-  public:
-    std::string GetGsidDir() const override { return "ota/test"s; }
-    std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
-    std::string GetSlotSuffix() const override { return slot_suffix_; }
-
-    void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
-
-  private:
-    std::string slot_suffix_ = "_a";
-};
-
-// These are not reset between each test because it's expensive to reconnect
-// to gsid each time.
+// These are not reset between each test because it's expensive to create
+// these resources (starting+connecting to gsid, zero-filling images).
 std::unique_ptr<SnapshotManager> sm;
 TestDeviceInfo* test_device = nullptr;
+std::string fake_super;
+
+static constexpr uint64_t kSuperSize = 16 * 1024 * 1024;
 
 class SnapshotTest : public ::testing::Test {
   public:
     SnapshotTest() : dm_(DeviceMapper::Instance()) {}
 
+    // This is exposed for main.
+    void Cleanup() {
+        InitializeState();
+        CleanupTestArtifacts();
+    }
+
   protected:
     void SetUp() override {
-        ASSERT_TRUE(sm->EnsureImageManager());
-        image_manager_ = sm->image_manager();
-
-        test_device->set_slot_suffix("_a");
-
+        ResetMockPropertyFetcher();
+        InitializeState();
         CleanupTestArtifacts();
+        FormatFakeSuper();
 
         ASSERT_TRUE(sm->BeginUpdate());
     }
@@ -77,6 +84,14 @@
         lock_ = nullptr;
 
         CleanupTestArtifacts();
+        ResetMockPropertyFetcher();
+    }
+
+    void InitializeState() {
+        ASSERT_TRUE(sm->EnsureImageManager());
+        image_manager_ = sm->image_manager();
+
+        test_device->set_slot_suffix("_a");
     }
 
     void CleanupTestArtifacts() {
@@ -87,7 +102,8 @@
         // get an accurate list to remove.
         lock_ = nullptr;
 
-        std::vector<std::string> snapshots = {"test-snapshot"};
+        std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
+                                              "test_partition_b"};
         for (const auto& snapshot : snapshots) {
             DeleteSnapshotDevice(snapshot);
             DeleteBackingImage(image_manager_, snapshot + "-cow");
@@ -96,11 +112,14 @@
             android::base::RemoveFileIfExists(status_file);
         }
 
-        // Remove all images. We hardcode the list of names since this can run
-        // before the test (when cleaning up from a crash).
-        std::vector<std::string> temp_partitions = {"base-device"};
-        for (const auto& partition : temp_partitions) {
-            DeleteBackingImage(image_manager_, partition);
+        // Remove stale partitions in fake super.
+        std::vector<std::string> partitions = {
+                "base-device",
+                "test_partition_b",
+                "test_partition_b-base",
+        };
+        for (const auto& partition : partitions) {
+            DeleteDevice(partition);
         }
 
         if (sm->GetUpdateState() != UpdateState::None) {
@@ -114,34 +133,98 @@
         return !!lock_;
     }
 
-    bool CreateTempDevice(const std::string& name, uint64_t size, std::string* path) {
-        if (!image_manager_->CreateBackingImage(name, size, false)) {
+    // 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) {
+        TestPartitionOpener opener(fake_super);
+        auto builder = MetadataBuilder::New(opener, "super", 0);
+        if (!builder) return false;
+
+        auto partition = builder->AddPartition(name, 0);
+        if (!partition) return false;
+        if (!builder->ResizePartition(partition, size)) {
             return false;
         }
-        return image_manager_->MapImageDevice(name, 10s, path);
+
+        // Update the source slot.
+        auto metadata = builder->Export();
+        if (!metadata) return false;
+        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);
+    }
+
+    bool MapUpdatePartitions() {
+        TestPartitionOpener opener(fake_super);
+        auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+        if (!builder) return false;
+
+        auto metadata = builder->Export();
+        if (!metadata) return false;
+
+        // Update the destination slot, mark it as updated.
+        if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
+            return false;
+        }
+
+        for (const auto& partition : metadata->partitions) {
+            CreateLogicalPartitionParams params = {
+                    .block_device = fake_super,
+                    .metadata = metadata.get(),
+                    .partition = &partition,
+                    .device_name = GetPartitionName(partition) + "-base",
+                    .force_writable = true,
+                    .timeout_ms = 10s,
+            };
+            std::string ignore_path;
+            if (!CreateLogicalPartition(params, &ignore_path)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     void DeleteSnapshotDevice(const std::string& snapshot) {
-        if (dm_.GetState(snapshot) != DmDeviceState::INVALID) {
-            ASSERT_TRUE(dm_.DeleteDevice(snapshot));
-        }
-        if (dm_.GetState(snapshot + "-inner") != DmDeviceState::INVALID) {
-            ASSERT_TRUE(dm_.DeleteDevice(snapshot + "-inner"));
-        }
+        DeleteDevice(snapshot);
+        DeleteDevice(snapshot + "-inner");
     }
-
-    void DeleteBackingImage(IImageManager* manager, const std::string& name) {
-        if (manager->IsImageMapped(name)) {
-            ASSERT_TRUE(manager->UnmapImageDevice(name));
-        }
-        if (manager->BackingImageExists(name)) {
-            ASSERT_TRUE(manager->DeleteBackingImage(name));
+    void DeleteDevice(const std::string& device) {
+        if (dm_.GetState(device) != DmDeviceState::INVALID) {
+            ASSERT_TRUE(dm_.DeleteDevice(device));
         }
     }
 
     DeviceMapper& dm_;
     std::unique_ptr<SnapshotManager::LockedFile> lock_;
     android::fiemap::IImageManager* image_manager_ = nullptr;
+    std::string fake_super_;
 };
 
 TEST_F(SnapshotTest, CreateSnapshot) {
@@ -177,7 +260,7 @@
                                    kDeviceSize));
 
     std::string base_device;
-    ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
 
     std::string snap_device;
     ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
@@ -193,7 +276,7 @@
                                    kSnapshotSize));
 
     std::string base_device;
-    ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
 
     std::string snap_device;
     ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
@@ -207,16 +290,40 @@
     ASSERT_FALSE(sm->InitiateMerge());
 }
 
+TEST_F(SnapshotTest, CleanFirstStageMount) {
+    // If there's no update in progress, there should be no first-stage mount
+    // needed.
+    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+    auto sm = SnapshotManager::NewForFirstStageMount(info);
+    ASSERT_NE(sm, nullptr);
+    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+}
+
+TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // We didn't change the slot, so we shouldn't need snapshots.
+    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+    auto sm = SnapshotManager::NewForFirstStageMount(info);
+    ASSERT_NE(sm, nullptr);
+    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+}
+
 TEST_F(SnapshotTest, Merge) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+            .WillByDefault(Return(true));
+
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
-                                   kDeviceSize));
 
     std::string base_device, snap_device;
-    ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+    ASSERT_TRUE(MapUpdatePartitions());
+    ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, 10s, &snap_device));
 
     std::string test_string = "This is a test string.";
     {
@@ -225,36 +332,38 @@
         ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
     }
 
-    // Note: we know the name of the device is test-snapshot because we didn't
-    // request a linear segment.
+    // Note: we know there is no inner/outer dm device since we didn't request
+    // a linear segment.
     DeviceMapper::TargetInfo target;
-    ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
     ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
 
-    // Set the state to Unverified, as if we finished an update.
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
-
     // Release the lock.
     lock_ = nullptr;
 
+    // Done updating.
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
     test_device->set_slot_suffix("_b");
     ASSERT_TRUE(sm->InitiateMerge());
 
     // The device should have been switched to a snapshot-merge target.
-    ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
     ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
 
     // We should not be able to cancel an update now.
     ASSERT_FALSE(sm->CancelUpdate());
 
-    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+    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-snapshot"));
+    ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
 
-    // Test that we can read back the string we wrote to the snapshot.
-    unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
+    // 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(snap_device.c_str(), O_RDONLY | O_CLOEXEC));
     ASSERT_GE(fd, 0);
 
     std::string buffer(test_string.size(), '\0');
@@ -270,7 +379,7 @@
                                    kDeviceSize));
 
     std::string base_device, snap_device;
-    ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
     ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
 
     // Keep an open handle to the cow device. This should cause the merge to
@@ -288,7 +397,7 @@
     ASSERT_TRUE(sm->InitiateMerge());
 
     // COW cannot be removed due to open fd, so expect a soft failure.
-    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot);
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeNeedsReboot);
 
     // Forcefully delete the snapshot device, so it looks like we just rebooted.
     DeleteSnapshotDevice("test-snapshot");
@@ -301,7 +410,133 @@
     fd = {};
     lock_ = nullptr;
 
-    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+}
+
+TEST_F(SnapshotTest, FirstStageMountAndMerge) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+            .WillByDefault(Return(true));
+
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+
+    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+    ASSERT_TRUE(MapUpdatePartitions());
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    // Simulate a reboot into the new slot.
+    lock_ = nullptr;
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+
+    auto rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_b");
+
+    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    ASSERT_TRUE(AcquireLock());
+
+    // Validate that we have a snapshot device.
+    SnapshotManager::SnapshotStatus status;
+    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
+    ASSERT_EQ(status.state, SnapshotManager::SnapshotState::Created);
+
+    DeviceMapper::TargetInfo target;
+    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+    ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+}
+
+TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+            .WillByDefault(Return(true));
+
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+
+    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+    ASSERT_TRUE(MapUpdatePartitions());
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    // Simulate a reboot into the new slot.
+    lock_ = nullptr;
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+
+    // Reflash the super partition.
+    FormatFakeSuper();
+    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
+
+    auto rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_b");
+
+    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    ASSERT_TRUE(AcquireLock());
+
+    SnapshotManager::SnapshotStatus status;
+    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
+
+    // We should not get a snapshot device now.
+    DeviceMapper::TargetInfo target;
+    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+    ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
+
+    // We should see a cancelled update as well.
+    lock_ = nullptr;
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
+}
+
+TEST_F(SnapshotTest, FlashSuperDuringMerge) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+            .WillByDefault(Return(true));
+
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+
+    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+    ASSERT_TRUE(MapUpdatePartitions());
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    // Simulate a reboot into the new slot.
+    lock_ = nullptr;
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+
+    auto rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_b");
+
+    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    ASSERT_TRUE(init->InitiateMerge());
+
+    // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
+    // status is still Merging.
+    DeleteSnapshotDevice("test_partition_b");
+    ASSERT_TRUE(init->image_manager()->UnmapImageDevice("test_partition_b-cow"));
+    FormatFakeSuper();
+    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    // Because the status is Merging, we must call ProcessUpdateState, which should
+    // detect a cancelled update.
+    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
+    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
 }
 
 }  // namespace snapshot
@@ -321,10 +556,14 @@
     ::testing::InitGoogleTest(&argc, argv);
 
     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) {
         if (!Mkdir(path)) {
@@ -336,9 +575,39 @@
     test_device = new TestDeviceInfo();
     sm = SnapshotManager::New(test_device);
     if (!sm) {
-        std::cerr << "Could not create snapshot manager";
+        std::cerr << "Could not create snapshot manager\n";
         return 1;
     }
 
-    return RUN_ALL_TESTS();
+    // Clean up previous run.
+    SnapshotTest().Cleanup();
+
+    // Use a separate image manager for our fake super partition.
+    auto super_images = IImageManager::Open("ota/test/super", 10s);
+    if (!super_images) {
+        std::cerr << "Could not create image manager\n";
+        return 1;
+    }
+
+    // Clean up any old copy.
+    DeleteBackingImage(super_images.get(), "fake-super");
+
+    // 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)) {
+        std::cerr << "Could not create fake super partition\n";
+        return 1;
+    }
+    if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
+        std::cerr << "Could not map fake super partition\n";
+        return 1;
+    }
+    test_device->set_fake_super(fake_super);
+
+    auto result = RUN_ALL_TESTS();
+
+    DeleteBackingImage(super_images.get(), "fake-super");
+
+    return result;
 }
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
new file mode 100644
index 0000000..f67dd21
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2019 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 "test_helpers.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace snapshot {
+
+using android::fiemap::IImageManager;
+
+void DeleteBackingImage(IImageManager* manager, const std::string& name) {
+    if (manager->IsImageMapped(name)) {
+        ASSERT_TRUE(manager->UnmapImageDevice(name));
+    }
+    if (manager->BackingImageExists(name)) {
+        ASSERT_TRUE(manager->DeleteBackingImage(name));
+    }
+}
+
+android::base::unique_fd TestPartitionOpener::Open(const std::string& partition_name,
+                                                   int flags) const {
+    if (partition_name == "super") {
+        return PartitionOpener::Open(fake_super_path_, flags);
+    }
+    return PartitionOpener::Open(partition_name, flags);
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name,
+                                  android::fs_mgr::BlockDeviceInfo* info) const {
+    if (partition_name == "super") {
+        return PartitionOpener::GetInfo(fake_super_path_, info);
+    }
+    return PartitionOpener::GetInfo(partition_name, info);
+}
+
+std::string TestPartitionOpener::GetDeviceString(const std::string& partition_name) const {
+    if (partition_name == "super") {
+        return fake_super_path_;
+    }
+    return PartitionOpener::GetDeviceString(partition_name);
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
new file mode 100644
index 0000000..9f582d9
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2019 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 <string>
+
+#include <libfiemap/image_manager.h>
+#include <liblp/partition_opener.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::string_literals;
+
+// Redirect requests for "super" to our fake super partition.
+class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
+  public:
+    explicit TestPartitionOpener(const std::string& fake_super_path)
+        : fake_super_path_(fake_super_path) {}
+
+    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+    bool GetInfo(const std::string& partition_name,
+                 android::fs_mgr::BlockDeviceInfo* info) const override;
+    std::string GetDeviceString(const std::string& partition_name) const override;
+
+  private:
+    std::string fake_super_path_;
+};
+
+class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
+  public:
+    TestDeviceInfo() {}
+    explicit TestDeviceInfo(const std::string& fake_super) { set_fake_super(fake_super); }
+    std::string GetGsidDir() const override { return "ota/test"s; }
+    std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
+    std::string GetSlotSuffix() const override { return slot_suffix_; }
+    std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
+        return *opener_.get();
+    }
+
+    void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
+    void set_fake_super(const std::string& path) {
+        opener_ = std::make_unique<TestPartitionOpener>(path);
+    }
+
+  private:
+    std::string slot_suffix_ = "_a";
+    std::unique_ptr<TestPartitionOpener> opener_;
+};
+
+// Helper for error-spam-free cleanup.
+void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/healthd/api/charger_sysprop-current.txt b/healthd/api/charger_sysprop-current.txt
new file mode 100644
index 0000000..678c847
--- /dev/null
+++ b/healthd/api/charger_sysprop-current.txt
@@ -0,0 +1,29 @@
+props {
+  module: "android.sysprop.ChargerProperties"
+  prop {
+    api_name: "disable_init_blank"
+    scope: Internal
+    prop_name: "ro.charger.disable_init_blank"
+  }
+  prop {
+    api_name: "draw_split_offset"
+    type: Long
+    scope: Internal
+    prop_name: "ro.charger.draw_split_offset"
+  }
+  prop {
+    api_name: "draw_split_screen"
+    scope: Internal
+    prop_name: "ro.charger.draw_split_screen"
+  }
+  prop {
+    api_name: "enable_suspend"
+    scope: Internal
+    prop_name: "ro.charger.enable_suspend"
+  }
+  prop {
+    api_name: "no_ui"
+    scope: Internal
+    prop_name: "ro.charger.no_ui"
+  }
+}
diff --git a/healthd/api/charger_sysprop-latest.txt b/healthd/api/charger_sysprop-latest.txt
new file mode 100644
index 0000000..678c847
--- /dev/null
+++ b/healthd/api/charger_sysprop-latest.txt
@@ -0,0 +1,29 @@
+props {
+  module: "android.sysprop.ChargerProperties"
+  prop {
+    api_name: "disable_init_blank"
+    scope: Internal
+    prop_name: "ro.charger.disable_init_blank"
+  }
+  prop {
+    api_name: "draw_split_offset"
+    type: Long
+    scope: Internal
+    prop_name: "ro.charger.draw_split_offset"
+  }
+  prop {
+    api_name: "draw_split_screen"
+    scope: Internal
+    prop_name: "ro.charger.draw_split_screen"
+  }
+  prop {
+    api_name: "enable_suspend"
+    scope: Internal
+    prop_name: "ro.charger.enable_suspend"
+  }
+  prop {
+    api_name: "no_ui"
+    scope: Internal
+    prop_name: "ro.charger.no_ui"
+  }
+}
diff --git a/healthd/api/current.txt b/healthd/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/removed.txt b/healthd/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/system-current.txt b/healthd/api/system-current.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/system-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/system-removed.txt b/healthd/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/test-current.txt b/healthd/api/test-current.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/test-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/test-removed.txt b/healthd/api/test-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/test-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/init/Android.bp b/init/Android.bp
index 38d495f..57555f6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -70,6 +70,7 @@
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
+        "libsnapshot_nobinder",
     ],
     shared_libs: [
         "libbacktrace",
@@ -117,6 +118,7 @@
         "firmware_handler.cpp",
         "first_stage_init.cpp",
         "first_stage_mount.cpp",
+        "fscrypt_init_extensions.cpp",
         "import_parser.cpp",
         "init.cpp",
         "interface_utils.cpp",
@@ -206,9 +208,20 @@
 // ------------------------------------------------------------------------------
 
 cc_test {
-    name: "init_tests",
+    name: "CtsInitTestCases",
     defaults: ["init_defaults"],
-    compile_multilib: "first",
+    require_root: true,
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
     srcs: [
         "devices_test.cpp",
         "init_test.cpp",
@@ -225,7 +238,12 @@
         "util_test.cpp",
     ],
     static_libs: ["libinit"],
-    test_suites: ["device-tests"],
+
+    test_suites: [
+        "cts",
+        "device-tests",
+        "vts",
+    ],
 }
 
 cc_benchmark {
diff --git a/init/Android.mk b/init/Android.mk
index 006e1bf..d7258a7 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -114,6 +114,7 @@
     libbacktrace \
     libmodprobe \
     libext2_uuid \
+    libsnapshot_nobinder \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
new file mode 100644
index 0000000..94a02e6
--- /dev/null
+++ b/init/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for CTS init test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="systems" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsInitTestCases" />
+        <option name="runtime-hint" value="65s" />
+    </test>
+</configuration>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a2d782b..7c66de5 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -55,7 +55,6 @@
 #include <cutils/android_reboot.h>
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
-#include <fscrypt/fscrypt_init_extensions.h>
 #include <libgsi/libgsi.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
@@ -64,6 +63,7 @@
 
 #include "action_manager.h"
 #include "bootchart.h"
+#include "fscrypt_init_extensions.h"
 #include "init.h"
 #include "mount_namespace.h"
 #include "parser.h"
@@ -356,51 +356,64 @@
 // mkdir <path> [mode] [owner] [group]
 static Result<void> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
-    if (args.size() >= 3) {
-        mode = std::strtoul(args[2].c_str(), 0, 8);
-    }
+    Result<uid_t> uid = -1;
+    Result<gid_t> gid = -1;
 
-    if (!make_dir(args[1], mode)) {
-        /* chmod in case the directory already exists */
-        if (errno == EEXIST) {
-            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
-                return ErrnoError() << "fchmodat() failed";
-            }
-        } else {
-            return ErrnoErrorIgnoreEnoent() << "mkdir() failed";
-        }
-    }
-
-    if (args.size() >= 4) {
-        auto uid = DecodeUid(args[3]);
-        if (!uid) {
-            return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
-        }
-        Result<gid_t> gid = -1;
-
-        if (args.size() == 5) {
+    switch (args.size()) {
+        case 5:
             gid = DecodeUid(args[4]);
             if (!gid) {
                 return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
             }
-        }
-
-        if (lchown(args[1].c_str(), *uid, *gid) == -1) {
-            return ErrnoError() << "lchown failed";
-        }
-
-        /* chown may have cleared S_ISUID and S_ISGID, chmod again */
-        if (mode & (S_ISUID | S_ISGID)) {
-            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
-                return ErrnoError() << "fchmodat failed";
+            FALLTHROUGH_INTENDED;
+        case 4:
+            uid = DecodeUid(args[3]);
+            if (!uid) {
+                return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
             }
+            FALLTHROUGH_INTENDED;
+        case 3:
+            mode = std::strtoul(args[2].c_str(), 0, 8);
+            FALLTHROUGH_INTENDED;
+        case 2:
+            break;
+        default:
+            return Error() << "Unexpected argument count: " << args.size();
+    }
+    std::string target = args[1];
+    struct stat mstat;
+    if (lstat(target.c_str(), &mstat) != 0) {
+        if (errno != ENOENT) {
+            return ErrnoError() << "lstat() failed on " << target;
+        }
+        if (!make_dir(target, mode)) {
+            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << target;
+        }
+        if (lstat(target.c_str(), &mstat) != 0) {
+            return ErrnoError() << "lstat() failed on new " << target;
         }
     }
-
+    if (!S_ISDIR(mstat.st_mode)) {
+        return Error() << "Not a directory on " << target;
+    }
+    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != mode;
+    if ((*uid != static_cast<uid_t>(-1) && *uid != mstat.st_uid) ||
+        (*gid != static_cast<gid_t>(-1) && *gid != mstat.st_gid)) {
+        if (lchown(target.c_str(), *uid, *gid) == -1) {
+            return ErrnoError() << "lchown failed on " << target;
+        }
+        // chown may have cleared S_ISUID and S_ISGID, chmod again
+        needs_chmod = true;
+    }
+    if (needs_chmod) {
+        if (fchmodat(AT_FDCWD, target.c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+            return ErrnoError() << "fchmodat() failed on " << target;
+        }
+    }
     if (fscrypt_is_native()) {
-        if (fscrypt_set_directory_policy(args[1].c_str())) {
+        if (fscrypt_set_directory_policy(target)) {
             return reboot_into_recovery(
-                {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
+                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + target});
         }
     }
     return {};
@@ -640,12 +653,7 @@
     if (!ReadFstabFromFile(fstab_file, &fstab)) {
         return Error() << "Could not read fstab";
     }
-
-    auto mount_fstab_return_code =
-            CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
-    if (!mount_fstab_return_code) {
-        return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
-    }
+    auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
     property_set(prop_name, std::to_string(t.duration().count()));
 
     if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
@@ -656,7 +664,7 @@
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+        auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
         if (!queue_fs_result) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
         }
@@ -672,13 +680,8 @@
         return Error() << "Could not read fstab";
     }
 
-    auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
-    if (!result) {
-        return Error() << "Could not call fs_mgr_mount_all() " << result.error();
-    }
-
-    if (*result != 0) {
-        return Error() << "fs_mgr_mount_all() failed: " << *result;
+    if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
+        return Error() << "umount_fstab() failed " << result;
     }
     return {};
 }
@@ -689,13 +692,8 @@
         return Error() << "Could not read fstab '" << args[1] << "'";
     }
 
-    auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
-    if (!result) {
-        return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
-    }
-
-    if (*result == 0) {
-        return Error() << "fs_mgr_swapon_all() failed.";
+    if (!fs_mgr_swapon_all(fstab)) {
+        return Error() << "fs_mgr_swapon_all() failed";
     }
 
     return {};
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1a5ed28..6b4216f 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -36,6 +36,7 @@
 #include <fs_mgr_overlayfs.h>
 #include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
 
 #include "devices.h"
 #include "switch_root.h"
@@ -55,6 +56,7 @@
 using android::fs_mgr::ReadDefaultFstab;
 using android::fs_mgr::ReadFstabFromDt;
 using android::fs_mgr::SkipMountingPartitions;
+using android::snapshot::SnapshotManager;
 
 using namespace std::literals;
 
@@ -244,8 +246,6 @@
 
     if (!InitDevices()) return false;
 
-    if (!CreateLogicalPartitions()) return false;
-
     if (!MountPartitions()) return false;
 
     return true;
@@ -366,6 +366,16 @@
         return false;
     }
 
+    if (SnapshotManager::IsSnapshotManagerNeeded()) {
+        auto sm = SnapshotManager::NewForFirstStageMount();
+        if (!sm) {
+            return false;
+        }
+        if (sm->NeedSnapshotsInFirstStageMount()) {
+            return sm->CreateLogicalAndSnapshotPartitions(lp_metadata_partition_);
+        }
+    }
+
     auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
     if (!metadata) {
         LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
@@ -493,14 +503,7 @@
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
-    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
-        return entry.mount_point == "/metadata";
-    });
-    if (metadata_partition != fstab_.end()) {
-        if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
-            UseGsiIfPresent();
-        }
-    }
+    UseGsiIfPresent();
 
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
@@ -523,6 +526,17 @@
 }
 
 bool FirstStageMount::MountPartitions() {
+    // Mount /metadata before creating logical partitions, since we need to
+    // know whether a snapshot merge is in progress.
+    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/metadata";
+    });
+    if (metadata_partition != fstab_.end()) {
+        MountPartition(metadata_partition, true /* erase_same_mounts */);
+    }
+
+    if (!CreateLogicalPartitions()) return false;
+
     if (!TrySwitchSystemAsRoot()) return false;
 
     if (!SkipMountingPartitions(&fstab_)) return false;
@@ -602,19 +616,11 @@
         return;
     }
 
-    // Find the name of the super partition for the GSI. It will either be
-    // "userdata", or a block device such as an sdcard. There are no by-name
-    // partitions other than userdata that we support installing GSIs to.
+    // Find the super name. PartitionOpener will ensure this translates to the
+    // correct block device path.
     auto super = GetMetadataSuperBlockDevice(*metadata.get());
-    std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
-    std::string super_path;
-    if (super_name == "userdata") {
-        super_path = "/dev/block/by-name/" + super_name;
-    } else {
-        super_path = "/dev/block/" + super_name;
-    }
-
-    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
+    auto super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_name)) {
         LOG(ERROR) << "GSI partition layout could not be instantiated";
         return;
     }
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
new file mode 100644
index 0000000..0f5a864
--- /dev/null
+++ b/init/fscrypt_init_extensions.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2016 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 "fscrypt_init_extensions.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fts.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <fscrypt/fscrypt.h>
+#include <keyutils.h>
+#include <logwrap/logwrap.h>
+
+#define TAG "fscrypt"
+
+static int set_system_de_policy_on(const std::string& dir);
+
+int fscrypt_install_keyring() {
+    key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
+
+    if (device_keyring == -1) {
+        PLOG(ERROR) << "Failed to create keyring";
+        return -1;
+    }
+
+    LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid();
+
+    return 0;
+}
+
+// TODO(b/139378601): use a single central implementation of this.
+static void delete_dir_contents(const std::string& dir) {
+    char* const paths[2] = {const_cast<char*>(dir.c_str()), nullptr};
+    FTS* fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+    FTSENT* cur;
+    while ((cur = fts_read(fts)) != nullptr) {
+        if (cur->fts_info == FTS_ERR) {
+            PLOG(ERROR) << "fts_read";
+            break;
+        }
+        if (dir == cur->fts_path) {
+            continue;
+        }
+        switch (cur->fts_info) {
+            case FTS_D:
+                break;  // Ignore these
+            case FTS_DP:
+                if (rmdir(cur->fts_path) == -1) {
+                    PLOG(ERROR) << "rmdir " << cur->fts_path;
+                }
+                break;
+            default:
+                PLOG(ERROR) << "FTS unexpected type " << cur->fts_info << " at " << cur->fts_path;
+                if (rmdir(cur->fts_path) != -1) break;
+                // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+                FALLTHROUGH_INTENDED;
+            case FTS_F:
+            case FTS_SL:
+            case FTS_SLNONE:
+                if (unlink(cur->fts_path) == -1) {
+                    PLOG(ERROR) << "unlink " << cur->fts_path;
+                }
+                break;
+        }
+    }
+
+    if (fts_close(fts) != 0) {
+        PLOG(ERROR) << "fts_close";
+    }
+}
+
+int fscrypt_set_directory_policy(const std::string& dir) {
+    const std::string prefix = "/data/";
+
+    if (!android::base::StartsWith(dir, prefix)) {
+        return 0;
+    }
+
+    // Special-case /data/media/obb per b/64566063
+    if (dir == "/data/media/obb") {
+        // Try to set policy on this directory, but if it is non-empty this may fail.
+        set_system_de_policy_on(dir);
+        return 0;
+    }
+
+    // Only set policy on first level /data directories
+    // To make this less restrictive, consider using a policy file.
+    // However this is overkill for as long as the policy is simply
+    // to apply a global policy to all /data folders created via makedir
+    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
+        return 0;
+    }
+
+    // Special case various directories that must not be encrypted,
+    // often because their subdirectories must be encrypted.
+    // This isn't a nice way to do this, see b/26641735
+    std::vector<std::string> directories_to_exclude = {
+        "lost+found",
+        "system_ce", "system_de",
+        "misc_ce", "misc_de",
+        "vendor_ce", "vendor_de",
+        "media",
+        "data", "user", "user_de",
+        "apex", "preloads", "app-staging",
+        "gsi",
+    };
+    for (const auto& d : directories_to_exclude) {
+        if ((prefix + d) == dir) {
+            LOG(INFO) << "Not setting policy on " << dir;
+            return 0;
+        }
+    }
+    int err = set_system_de_policy_on(dir);
+    if (err == 0) {
+        return 0;
+    }
+    // Empty these directories if policy setting fails.
+    std::vector<std::string> wipe_on_failure = {
+            "rollback", "rollback-observer",  // b/139193659
+    };
+    for (const auto& d : wipe_on_failure) {
+        if ((prefix + d) == dir) {
+            LOG(ERROR) << "Setting policy failed, deleting: " << dir;
+            delete_dir_contents(dir);
+            err = set_system_de_policy_on(dir);
+            break;
+        }
+    }
+    return err;
+}
+
+static int set_system_de_policy_on(const std::string& dir) {
+    std::string ref_filename = std::string("/data") + fscrypt_key_ref;
+    std::string policy;
+    if (!android::base::ReadFileToString(ref_filename, &policy)) {
+        LOG(ERROR) << "Unable to read system policy to set on " << dir;
+        return -1;
+    }
+
+    auto type_filename = std::string("/data") + fscrypt_key_mode;
+    std::string modestring;
+    if (!android::base::ReadFileToString(type_filename, &modestring)) {
+        LOG(ERROR) << "Cannot read mode";
+    }
+
+    std::vector<std::string> modes = android::base::Split(modestring, ":");
+
+    if (modes.size() < 1 || modes.size() > 2) {
+        LOG(ERROR) << "Invalid encryption mode string: " << modestring;
+        return -1;
+    }
+
+    LOG(INFO) << "Setting policy on " << dir;
+    int result =
+            fscrypt_policy_ensure(dir.c_str(), policy.c_str(), policy.length(), modes[0].c_str(),
+                                  modes.size() >= 2 ? modes[1].c_str() : "aes-256-cts");
+    if (result) {
+        LOG(ERROR) << android::base::StringPrintf("Setting %02x%02x%02x%02x policy on %s failed!",
+                                                  policy[0], policy[1], policy[2], policy[3],
+                                                  dir.c_str());
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/init/fscrypt_init_extensions.h b/init/fscrypt_init_extensions.h
new file mode 100644
index 0000000..2163ef6
--- /dev/null
+++ b/init/fscrypt_init_extensions.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 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 <string>
+
+int fscrypt_install_keyring();
+int fscrypt_set_directory_policy(const std::string& dir);
diff --git a/init/init.cpp b/init/init.cpp
index 18fb0c3..cf04a2e 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -715,6 +715,7 @@
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
 
+    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
@@ -722,7 +723,6 @@
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
-    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     Keychords keychords;
     am.QueueBuiltinAction(
             [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 17622a3..3408ff3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,7 +39,6 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include <atomic>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -53,7 +52,6 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <property_info_parser/property_info_parser.h>
 #include <property_info_serializer/property_info_serializer.h>
 #include <selinux/android.h>
@@ -61,6 +59,7 @@
 #include <selinux/selinux.h>
 
 #include "debug_ramdisk.h"
+#include "epoll.h"
 #include "init.h"
 #include "persistent_properties.h"
 #include "property_type.h"
@@ -77,7 +76,6 @@
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::Trim;
-using android::base::unique_fd;
 using android::base::WriteStringToFile;
 using android::properties::BuildTrie;
 using android::properties::ParsePropertyInfoFile;
@@ -1004,42 +1002,5 @@
     }
 }
 
-Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
-    unique_fd reader;
-    unique_fd writer;
-    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
-        return ErrnoError() << "Could not create socket pair";
-    }
-
-    int result = 0;
-    std::atomic<bool> end = false;
-    auto thread = std::thread{[&f, &result, &end, &writer] {
-        result = f();
-        end = true;
-        send(writer, "1", 1, 0);
-    }};
-
-    Epoll epoll;
-    if (auto result = epoll.Open(); !result) {
-        return Error() << "Could not create epoll: " << result.error();
-    }
-    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
-        return Error() << "Could not register epoll handler for property fd: " << result.error();
-    }
-
-    // No-op function, just used to break from loop.
-    if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
-        return Error() << "Could not register epoll handler for ending thread:" << result.error();
-    }
-
-    while (!end) {
-        epoll.Wait({});
-    }
-
-    thread.join();
-
-    return result;
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index dc47b4d..7f9f844 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,11 +18,9 @@
 
 #include <sys/socket.h>
 
-#include <functional>
 #include <string>
 
 #include "epoll.h"
-#include "result.h"
 
 namespace android {
 namespace init {
@@ -39,13 +37,5 @@
 void load_persist_props();
 void StartPropertyService(Epoll* epoll);
 
-template <typename F, typename... Args>
-Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
-    Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
-
-    auto func = [&] { return f(args...); };
-    return CallFunctionAndHandlePropertiesImpl(func);
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index c038aff..0f4cd0d 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -56,6 +56,11 @@
 }
 
 TEST(property_service, non_utf8_value) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Skipping test, must be run as root.";
+        return;
+    }
+
     ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
     EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
     EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
diff --git a/init/selinux.cpp b/init/selinux.cpp
index fd42256..6842820 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -51,6 +51,8 @@
 
 #include <android/api-level.h>
 #include <fcntl.h>
+#include <linux/audit.h>
+#include <linux/netlink.h>
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -446,6 +448,35 @@
     }
 }
 
+constexpr size_t kKlogMessageSize = 1024;
+
+void SelinuxAvcLog(char* buf, size_t buf_len) {
+    CHECK_GT(buf_len, 0u);
+
+    size_t str_len = strnlen(buf, buf_len);
+    // trim newline at end of string
+    if (buf[str_len - 1] == '\n') {
+        buf[str_len - 1] = '\0';
+    }
+
+    struct NetlinkMessage {
+        nlmsghdr hdr;
+        char buf[kKlogMessageSize];
+    } request = {};
+
+    request.hdr.nlmsg_flags = NLM_F_REQUEST;
+    request.hdr.nlmsg_type = AUDIT_USER_AVC;
+    request.hdr.nlmsg_len = sizeof(request);
+    strlcpy(request.buf, buf, sizeof(request.buf));
+
+    auto fd = unique_fd{socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT)};
+    if (!fd.ok()) {
+        return;
+    }
+
+    TEMP_FAILURE_RETRY(send(fd, &request, sizeof(request), 0));
+}
+
 }  // namespace
 
 // The files and directories that were created before initial sepolicy load or
@@ -478,12 +509,19 @@
     } else if (type == SELINUX_INFO) {
         severity = android::base::INFO;
     }
-    char buf[1024];
+    char buf[kKlogMessageSize];
     va_list ap;
     va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
+    int length_written = vsnprintf(buf, sizeof(buf), fmt, ap);
     va_end(ap);
-    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    if (length_written <= 0) {
+        return 0;
+    }
+    if (type == SELINUX_AVC) {
+        SelinuxAvcLog(buf, sizeof(buf));
+    } else {
+        android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    }
     return 0;
 }
 
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index ae89c38..dcbff82 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -43,7 +43,7 @@
 template <typename F>
 void RunTest(F&& test_function) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 8b2cf62..cffc1b9 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -17,12 +17,15 @@
 #include "ueventd.h"
 
 #include <ctype.h>
+#include <dirent.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <set>
 #include <thread>
@@ -121,8 +124,9 @@
     void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
     void RegenerateUevents();
     void ForkSubProcesses();
-    void DoRestoreCon();
     void WaitForSubProcesses();
+    void RestoreConHandler(unsigned int process_num, unsigned int total_processes);
+    void GenerateRestoreCon(const std::string& directory);
 
     UeventListener& uevent_listener_;
     std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
@@ -131,6 +135,8 @@
     std::vector<Uevent> uevent_queue_;
 
     std::set<pid_t> subprocess_pids_;
+
+    std::vector<std::string> restorecon_queue_;
 };
 
 void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
@@ -141,9 +147,38 @@
             uevent_handler->HandleUevent(uevent);
         }
     }
+}
+
+void ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {
+    for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {
+        auto& dir = restorecon_queue_[i];
+
+        selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+    }
     _exit(EXIT_SUCCESS);
 }
 
+void ColdBoot::GenerateRestoreCon(const std::string& directory) {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
+
+    if (!dir) return;
+
+    struct dirent* dent;
+    while ((dent = readdir(dir.get())) != NULL) {
+        if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue;
+
+        struct stat st;
+        if (fstatat(dirfd(dir.get()), dent->d_name, &st, 0) == -1) continue;
+
+        if (S_ISDIR(st.st_mode)) {
+            std::string fullpath = directory + "/" + dent->d_name;
+            if (fullpath != "/sys/devices") {
+                restorecon_queue_.emplace_back(fullpath);
+            }
+        }
+    }
+}
+
 void ColdBoot::RegenerateUevents() {
     uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
         uevent_queue_.emplace_back(uevent);
@@ -160,16 +195,13 @@
 
         if (pid == 0) {
             UeventHandlerMain(i, num_handler_subprocesses_);
+            RestoreConHandler(i, num_handler_subprocesses_);
         }
 
         subprocess_pids_.emplace(pid);
     }
 }
 
-void ColdBoot::DoRestoreCon() {
-    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
 void ColdBoot::WaitForSubProcesses() {
     // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
     // stuck.
@@ -208,9 +240,13 @@
 
     RegenerateUevents();
 
-    ForkSubProcesses();
+    selinux_android_restorecon("/sys", 0);
+    selinux_android_restorecon("/sys/devices", 0);
+    GenerateRestoreCon("/sys");
+    // takes long time for /sys/devices, parallelize it
+    GenerateRestoreCon("/sys/devices");
 
-    DoRestoreCon();
+    ForkSubProcesses();
 
     WaitForSubProcesses();
 
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index bfdc28e..2d7d2f8 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -68,7 +68,7 @@
 
 TEST(ueventd, setegid_IsPerThread) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
 
@@ -92,11 +92,11 @@
 
 TEST(ueventd, setfscreatecon_IsPerThread) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
     if (!is_selinux_enabled() || security_getenforce() == 1) {
-        GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+        GTEST_SKIP() << "Skipping test, SELinux must be enabled and in permissive mode.";
         return;
     }
 
@@ -127,7 +127,7 @@
 
 TEST(ueventd, selabel_lookup_MultiThreaded) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
 
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 53d3ab3..c38d8cd 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,7 +15,6 @@
 //
 
 liblog_sources = [
-    "config_read.cpp",
     "config_write.cpp",
     "log_event_list.cpp",
     "log_event_write.cpp",
diff --git a/liblog/config_read.cpp b/liblog/config_read.cpp
deleted file mode 100644
index 3139f78..0000000
--- a/liblog/config_read.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 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 <log/log_transport.h>
-
-#include "config_read.h"
-#include "logger.h"
-
-struct listnode __android_log_transport_read = {&__android_log_transport_read,
-                                                &__android_log_transport_read};
-struct listnode __android_log_persist_read = {&__android_log_persist_read,
-                                              &__android_log_persist_read};
-
-static void __android_log_add_transport(struct listnode* list,
-                                        struct android_log_transport_read* transport) {
-  uint32_t i;
-
-  /* Try to keep one functioning transport for each log buffer id */
-  for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
-    struct android_log_transport_read* transp;
-
-    if (list_empty(list)) {
-      if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
-        list_add_tail(list, &transport->node);
-        return;
-      }
-    } else {
-      read_transport_for_each(transp, list) {
-        if (!transp->available) {
-          return;
-        }
-        if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
-            (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
-          list_add_tail(list, &transport->node);
-          return;
-        }
-      }
-    }
-  }
-}
-
-void __android_log_config_read() {
-#if (FAKE_LOG_DEVICE == 0)
-  if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
-    extern struct android_log_transport_read logdLoggerRead;
-    extern struct android_log_transport_read pmsgLoggerRead;
-
-    __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
-    __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
-  }
-#endif
-}
-
-void __android_log_config_read_close() {
-  struct android_log_transport_read* transport;
-  struct listnode* n;
-
-  read_transport_for_each_safe(transport, n, &__android_log_transport_read) {
-    list_remove(&transport->node);
-  }
-  read_transport_for_each_safe(transport, n, &__android_log_persist_read) {
-    list_remove(&transport->node);
-  }
-}
diff --git a/liblog/config_read.h b/liblog/config_read.h
deleted file mode 100644
index 212b8a0..0000000
--- a/liblog/config_read.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 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 <cutils/list.h>
-
-#include "log_portability.h"
-
-__BEGIN_DECLS
-
-extern struct listnode __android_log_transport_read;
-extern struct listnode __android_log_persist_read;
-
-#define read_transport_for_each(transp, transports)                           \
-  for ((transp) = node_to_item((transports)->next,                            \
-                               struct android_log_transport_read, node);      \
-       ((transp) != node_to_item((transports),                                \
-                                 struct android_log_transport_read, node)) && \
-       ((transp) != node_to_item((transp)->node.next,                         \
-                                 struct android_log_transport_read, node));   \
-       (transp) = node_to_item((transp)->node.next,                           \
-                               struct android_log_transport_read, node))
-
-#define read_transport_for_each_safe(transp, n, transports)                   \
-  for ((transp) = node_to_item((transports)->next,                            \
-                               struct android_log_transport_read, node),      \
-      (n) = (transp)->node.next;                                              \
-       ((transp) != node_to_item((transports),                                \
-                                 struct android_log_transport_read, node)) && \
-       ((transp) !=                                                           \
-        node_to_item((n), struct android_log_transport_read, node));          \
-       (transp) = node_to_item((n), struct android_log_transport_read, node), \
-      (n) = (transp)->node.next)
-
-void __android_log_config_read();
-void __android_log_config_read_close();
-
-__END_DECLS
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index b7ba782..eba305f 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,7 +35,6 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "config_read.h"
 #include "log_portability.h"
 #include "logd_reader.h"
 #include "logger.h"
diff --git a/liblog/logger.h b/liblog/logger.h
index accb6e7..4b4ef5f 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -95,9 +95,19 @@
                       size_t len);
 };
 
+struct android_log_transport_context {
+  union android_log_context_union context; /* zero init per-transport context */
+
+  struct android_log_transport_read* transport;
+  unsigned logMask;      /* mask of requested log buffers */
+  int ret;               /* return value associated with following data */
+  struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
+};
+
 struct android_log_logger_list {
   struct listnode logger;
-  struct listnode transport;
+  android_log_transport_context transport_context;
+  bool transport_initialized;
   int mode;
   unsigned int tail;
   log_time start;
@@ -111,27 +121,7 @@
   log_id_t logId;
 };
 
-struct android_log_transport_context {
-  struct listnode node;
-  union android_log_context_union context; /* zero init per-transport context */
-  struct android_log_logger_list* parent;
-
-  struct android_log_transport_read* transport;
-  unsigned logMask;      /* mask of requested log buffers */
-  int ret;               /* return value associated with following data */
-  struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
-};
-
 /* assumes caller has structures read-locked, single threaded, or fenced */
-#define transport_context_for_each(transp, logger_list)                          \
-  for ((transp) = node_to_item((logger_list)->transport.next,                    \
-                               struct android_log_transport_context, node);      \
-       ((transp) != node_to_item(&(logger_list)->transport,                      \
-                                 struct android_log_transport_context, node)) && \
-       ((transp)->parent == (logger_list));                                      \
-       (transp) = node_to_item((transp)->node.next,                              \
-                               struct android_log_transport_context, node))
-
 #define logger_for_each(logp, logger_list)                          \
   for ((logp) = node_to_item((logger_list)->logger.next,            \
                              struct android_log_logger, node);      \
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 4cf0846..ff816b7 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -29,7 +29,6 @@
 #include <cutils/list.h>
 #include <private/android_filesystem_config.h>
 
-#include "config_read.h"
 #include "log_portability.h"
 #include "logger.h"
 
@@ -55,9 +54,6 @@
 }
 
 static int init_transport_context(struct android_log_logger_list* logger_list) {
-  struct android_log_transport_read* transport;
-  struct listnode* node;
-
   if (!logger_list) {
     return -EINVAL;
   }
@@ -66,77 +62,63 @@
     return -EINVAL;
   }
 
-  if (!list_empty(&logger_list->transport)) {
+  if (logger_list->transport_initialized) {
     return 0;
   }
 
-  __android_log_lock();
-  /* mini __write_to_log_initialize() to populate transports */
-  if (list_empty(&__android_log_transport_read) && list_empty(&__android_log_persist_read)) {
-    __android_log_config_read();
-  }
-  __android_log_unlock();
+#if (FAKE_LOG_DEVICE == 0)
+  extern struct android_log_transport_read logdLoggerRead;
+  extern struct android_log_transport_read pmsgLoggerRead;
 
-  node = (logger_list->mode & ANDROID_LOG_PSTORE) ? &__android_log_persist_read
-                                                  : &__android_log_transport_read;
+  struct android_log_transport_read* transport;
+  transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
 
-  read_transport_for_each(transport, node) {
-    struct android_log_transport_context* transp;
-    struct android_log_logger* logger;
-    unsigned logMask = 0;
+  struct android_log_logger* logger;
+  unsigned logMask = 0;
 
-    logger_for_each(logger, logger_list) {
-      log_id_t logId = logger->logId;
+  logger_for_each(logger, logger_list) {
+    log_id_t logId = logger->logId;
 
-      if ((logId == LOG_ID_SECURITY) && (__android_log_uid() != AID_SYSTEM)) {
-        continue;
-      }
-      if (transport->read && (!transport->available || (transport->available(logId) >= 0))) {
-        logMask |= 1 << logId;
-      }
-    }
-    if (!logMask) {
+    if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
       continue;
     }
-    transp = static_cast<android_log_transport_context*>(calloc(1, sizeof(*transp)));
-    if (!transp) {
-      return -ENOMEM;
+    if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
+      logMask |= 1 << logId;
     }
-    transp->parent = logger_list;
-    transp->transport = transport;
-    transp->logMask = logMask;
-    transp->ret = 1;
-    list_add_tail(&logger_list->transport, &transp->node);
   }
-  if (list_empty(&logger_list->transport)) {
+  if (!logMask) {
     return -ENODEV;
   }
+
+  logger_list->transport_context.transport = transport;
+  logger_list->transport_context.logMask = logMask;
+  logger_list->transport_context.ret = 1;
+#endif
   return 0;
 }
 
-#define LOGGER_FUNCTION(logger, def, func, args...)                                  \
-  ssize_t ret = -EINVAL;                                                             \
-  struct android_log_transport_context* transp;                                      \
-  struct android_log_logger* logger_internal = (struct android_log_logger*)(logger); \
-                                                                                     \
-  if (!logger_internal) {                                                            \
-    return ret;                                                                      \
-  }                                                                                  \
-  ret = init_transport_context(logger_internal->parent);                             \
-  if (ret < 0) {                                                                     \
-    return ret;                                                                      \
-  }                                                                                  \
-                                                                                     \
-  ret = (def);                                                                       \
-  transport_context_for_each(transp, logger_internal->parent) {                      \
-    if ((transp->logMask & (1 << logger_internal->logId)) && transp->transport &&    \
-        transp->transport->func) {                                                   \
-      ssize_t retval = (transp->transport->func)(logger_internal, transp, ##args);   \
-      if ((ret >= 0) || (ret == (def))) {                                            \
-        ret = retval;                                                                \
-      }                                                                              \
-    }                                                                                \
-  }                                                                                  \
+#define LOGGER_FUNCTION(logger, def, func, args...)                                               \
+  ssize_t ret = -EINVAL;                                                                          \
+  android_log_logger* logger_internal = reinterpret_cast<android_log_logger*>(logger);            \
+                                                                                                  \
+  if (!logger_internal) {                                                                         \
+    return ret;                                                                                   \
+  }                                                                                               \
+  ret = init_transport_context(logger_internal->parent);                                          \
+  if (ret < 0) {                                                                                  \
+    return ret;                                                                                   \
+  }                                                                                               \
+                                                                                                  \
+  ret = (def);                                                                                    \
+  android_log_transport_context* transport_context = &logger_internal->parent->transport_context; \
+  if (transport_context->logMask & (1 << logger_internal->logId) &&                               \
+      transport_context->transport && transport_context->transport->func) {                       \
+    ssize_t retval =                                                                              \
+        (transport_context->transport->func)(logger_internal, transport_context, ##args);         \
+    if (ret >= 0 || ret == (def)) {                                                               \
+      ret = retval;                                                                               \
+    }                                                                                             \
+  }                                                                                               \
   return ret
 
 int android_logger_clear(struct logger* logger) {
@@ -167,25 +149,24 @@
   LOGGER_FUNCTION(logger, 4, version);
 }
 
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                           \
-  struct android_log_transport_context* transp;                                         \
-  struct android_log_logger_list* logger_list_internal =                                \
-      (struct android_log_logger_list*)(logger_list);                                   \
-                                                                                        \
-  ssize_t ret = init_transport_context(logger_list_internal);                           \
-  if (ret < 0) {                                                                        \
-    return ret;                                                                         \
-  }                                                                                     \
-                                                                                        \
-  ret = (def);                                                                          \
-  transport_context_for_each(transp, logger_list_internal) {                            \
-    if (transp->transport && (transp->transport->func)) {                               \
-      ssize_t retval = (transp->transport->func)(logger_list_internal, transp, ##args); \
-      if ((ret >= 0) || (ret == (def))) {                                               \
-        ret = retval;                                                                   \
-      }                                                                                 \
-    }                                                                                   \
-  }                                                                                     \
+#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                                  \
+  android_log_logger_list* logger_list_internal =                                              \
+      reinterpret_cast<android_log_logger_list*>(logger_list);                                 \
+                                                                                               \
+  ssize_t ret = init_transport_context(logger_list_internal);                                  \
+  if (ret < 0) {                                                                               \
+    return ret;                                                                                \
+  }                                                                                            \
+                                                                                               \
+  ret = (def);                                                                                 \
+  android_log_transport_context* transport_context = &logger_list_internal->transport_context; \
+  if (transport_context->transport && transport_context->transport->func) {                    \
+    ssize_t retval =                                                                           \
+        (transport_context->transport->func)(logger_list_internal, transport_context, ##args); \
+    if (ret >= 0 || ret == (def)) {                                                            \
+      ret = retval;                                                                            \
+    }                                                                                          \
+  }                                                                                            \
   return ret
 
 /*
@@ -212,7 +193,6 @@
   }
 
   list_init(&logger_list->logger);
-  list_init(&logger_list->transport);
   logger_list->mode = mode;
   logger_list->tail = tail;
   logger_list->pid = pid;
@@ -229,7 +209,6 @@
   }
 
   list_init(&logger_list->logger);
-  list_init(&logger_list->transport);
   logger_list->mode = mode;
   logger_list->start = start;
   logger_list->pid = pid;
@@ -247,38 +226,27 @@
   struct android_log_logger* logger;
 
   if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
-    goto err;
+    return nullptr;
   }
 
   logger_for_each(logger, logger_list_internal) {
     if (logger->logId == logId) {
-      goto ok;
+      return reinterpret_cast<struct logger*>(logger);
     }
   }
 
   logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
   if (!logger) {
-    goto err;
+    return nullptr;
   }
 
   logger->logId = logId;
   list_add_tail(&logger_list_internal->logger, &logger->node);
   logger->parent = logger_list_internal;
 
-  /* Reset known transports to re-evaluate, we just added one */
-  while (!list_empty(&logger_list_internal->transport)) {
-    struct listnode* node = list_head(&logger_list_internal->transport);
-    struct android_log_transport_context* transp =
-        node_to_item(node, struct android_log_transport_context, node);
+  // Reset known transport to re-evaluate, since we added a new logger.
+  logger_list_internal->transport_initialized = false;
 
-    list_remove(&transp->node);
-    free(transp);
-  }
-  goto ok;
-
-err:
-  logger = NULL;
-ok:
   return (struct logger*)logger;
 }
 
@@ -340,7 +308,6 @@
 
 /* Read from the selected logs */
 int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
-  struct android_log_transport_context* transp;
   struct android_log_logger_list* logger_list_internal =
       (struct android_log_logger_list*)logger_list;
 
@@ -349,84 +316,8 @@
     return ret;
   }
 
-  /* at least one transport */
-  transp = node_to_item(logger_list_internal->transport.next, struct android_log_transport_context,
-                        node);
-
-  /* more than one transport? */
-  if (transp->node.next != &logger_list_internal->transport) {
-    /* Poll and merge sort the entries if from multiple transports */
-    struct android_log_transport_context* oldest = NULL;
-    int ret;
-    int polled = 0;
-    do {
-      if (polled) {
-        sched_yield();
-      }
-      ret = -1000;
-      polled = 0;
-      do {
-        int retval = transp->ret;
-        if ((retval > 0) && !transp->logMsg.entry.len) {
-          if (!transp->transport->read) {
-            retval = transp->ret = 0;
-          } else if ((logger_list_internal->mode & ANDROID_LOG_NONBLOCK) ||
-                     !transp->transport->poll) {
-            retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
-          } else {
-            int pollval = (*transp->transport->poll)(logger_list_internal, transp);
-            if (pollval <= 0) {
-              sched_yield();
-              pollval = (*transp->transport->poll)(logger_list_internal, transp);
-            }
-            polled = 1;
-            if (pollval < 0) {
-              if ((pollval == -EINTR) || (pollval == -EAGAIN)) {
-                return -EAGAIN;
-              }
-              retval = transp->ret = pollval;
-            } else if (pollval > 0) {
-              retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
-            }
-          }
-        }
-        if (ret < retval) {
-          ret = retval;
-        }
-        if ((transp->ret > 0) && transp->logMsg.entry.len &&
-            (!oldest || (oldest->logMsg.entry.sec > transp->logMsg.entry.sec) ||
-             ((oldest->logMsg.entry.sec == transp->logMsg.entry.sec) &&
-              (oldest->logMsg.entry.nsec > transp->logMsg.entry.nsec)))) {
-          oldest = transp;
-        }
-        transp = node_to_item(transp->node.next, struct android_log_transport_context, node);
-      } while (transp != node_to_item(&logger_list_internal->transport,
-                                      struct android_log_transport_context, node));
-      if (!oldest && (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
-        return (ret < 0) ? ret : -EAGAIN;
-      }
-      transp = node_to_item(logger_list_internal->transport.next,
-                            struct android_log_transport_context, node);
-    } while (!oldest && (ret > 0));
-    if (!oldest) {
-      return ret;
-    }
-    // ret is a positive value less than sizeof(struct log_msg)
-    ret = oldest->ret;
-    if (ret < oldest->logMsg.entry.hdr_size) {
-      // zero truncated header fields.
-      memset(
-          log_msg, 0,
-          (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg) ? sizeof(oldest->logMsg)
-                                                                  : oldest->logMsg.entry.hdr_size));
-    }
-    memcpy(log_msg, &oldest->logMsg, ret);
-    oldest->logMsg.entry.len = 0; /* Mark it as copied */
-    return ret;
-  }
-
-  /* if only one, no need to copy into transport_context and merge-sort */
-  return android_transport_read(logger_list_internal, transp, log_msg);
+  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
+  return android_transport_read(logger_list_internal, transport_context, log_msg);
 }
 
 /* Close all the logs */
@@ -438,16 +329,10 @@
     return;
   }
 
-  while (!list_empty(&logger_list_internal->transport)) {
-    struct listnode* node = list_head(&logger_list_internal->transport);
-    struct android_log_transport_context* transp =
-        node_to_item(node, struct android_log_transport_context, node);
+  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
 
-    if (transp->transport && transp->transport->close) {
-      (*transp->transport->close)(logger_list_internal, transp);
-    }
-    list_remove(&transp->node);
-    free(transp);
+  if (transport_context->transport && transport_context->transport->close) {
+    (*transport_context->transport->close)(logger_list_internal, transport_context);
   }
 
   while (!list_empty(&logger_list_internal->logger)) {
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index a4b3cd7..4fbab4b 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -29,7 +29,6 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "config_read.h" /* __android_log_config_read_close() definition */
 #include "config_write.h"
 #include "log_portability.h"
 #include "logger.h"
@@ -624,7 +623,6 @@
   if (__android_log_transport != transport_flag) {
     __android_log_transport = transport_flag;
     __android_log_config_write_close();
-    __android_log_config_read_close();
 
     write_to_log = __write_to_log_init;
     /* generically we only expect these two values for write_to_log */
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index eaac97f..81563bc 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -24,7 +24,6 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "config_read.h"
 #include "logger.h"
 
 static int pmsgAvailable(log_id_t logId);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 1f87b3e..45a9bc9 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1062,6 +1062,7 @@
 when you depart from me, sorrow abides and happiness\n\
 takes his leave.";
 
+#ifdef USING_LOGGER_DEFAULT
 TEST(liblog, max_payload) {
 #ifdef TEST_PREFIX
   TEST_PREFIX
@@ -1130,6 +1131,7 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif
 
 TEST(liblog, __android_log_buf_print__maxtag) {
 #ifdef TEST_PREFIX
@@ -1271,6 +1273,7 @@
 #endif
 }
 
+#ifdef USING_LOGGER_DEFAULT
 TEST(liblog, dual_reader) {
 #ifdef TEST_PREFIX
   TEST_PREFIX
@@ -1334,6 +1337,7 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif
 
 #ifdef USING_LOGGER_DEFAULT  // Do not retest logprint
 static bool checkPriForTag(AndroidLogFormat* p_format, const char* tag,
@@ -3004,21 +3008,6 @@
   EXPECT_LE(0, android_log_destroy(&ctx));
   ASSERT_TRUE(NULL == ctx);
 }
-
-TEST(liblog, android_log_write_list_buffer) {
-  __android_log_event_list ctx(1005);
-  ctx << 1005 << "tag_def"
-      << "(tag|1),(name|3),(format|3)";
-  std::string buffer(ctx);
-  ctx.close();
-
-  char msgBuf[1024];
-  memset(msgBuf, 0, sizeof(msgBuf));
-  EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(), msgBuf,
-                                         sizeof(msgBuf)),
-            0);
-  EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
-}
 #endif  // USING_LOGGER_DEFAULT
 
 #ifdef USING_LOGGER_DEFAULT  // Do not retest pmsg functionality
diff --git a/liblog/tests/liblog_test_default.cpp b/liblog/tests/liblog_test_default.cpp
index 9fc443c..2edea27 100644
--- a/liblog/tests/liblog_test_default.cpp
+++ b/liblog/tests/liblog_test_default.cpp
@@ -2,4 +2,5 @@
 #include <log/log_transport.h>
 #define TEST_LOGGER LOGGER_DEFAULT
 #endif
+#define USING_LOGGER_DEFAULT
 #include "liblog_test.cpp"
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 62a7266..d864d1b 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -105,6 +105,8 @@
 cc_test {
     name: "memunreachable_binder_test",
     defaults: ["libmemunreachable_defaults"],
+    require_root: true,
+
     srcs: [
         "tests/Binder_test.cpp",
     ],
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15f03d0..0c9a2b8 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -34,6 +34,7 @@
     host_supported: true,
     srcs: [
         "process.cpp",
+        "process_map.cpp",
     ],
 
     local_include_dirs: ["include"],
@@ -58,6 +59,7 @@
     name: "libprocinfo_test",
     defaults: ["libprocinfo_defaults"],
     host_supported: true,
+    isolated: true,
     srcs: [
         "process_test.cpp",
         "process_map_test.cpp",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index b6ec3cb..569a022 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -176,5 +176,9 @@
                const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
 }
 
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+                          const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                   const char*)>& callback);
+
 } /* namespace procinfo */
 } /* namespace android */
diff --git a/libprocinfo/process_map.cpp b/libprocinfo/process_map.cpp
new file mode 100644
index 0000000..5e240b9
--- /dev/null
+++ b/libprocinfo/process_map.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 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 <procinfo/process_map.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <procinfo/process.h>
+
+namespace android {
+namespace procinfo {
+
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+                          const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                   const char*)>& callback) {
+  if (buffer == nullptr || buffer_size == 0) {
+    return false;
+  }
+
+  int fd = open(map_file, O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    return false;
+  }
+
+  char* char_buffer = reinterpret_cast<char*>(buffer);
+  size_t start = 0;
+  size_t read_bytes = 0;
+  char* line = nullptr;
+  bool read_complete = false;
+  while (true) {
+    ssize_t bytes =
+        TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
+    if (bytes <= 0) {
+      if (read_bytes == 0) {
+        close(fd);
+        return bytes == 0;
+      }
+      // Treat the last piece of data as the last line.
+      char_buffer[start + read_bytes] = '\n';
+      bytes = 1;
+      read_complete = true;
+    }
+    read_bytes += bytes;
+
+    while (read_bytes > 0) {
+      char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
+      if (newline == nullptr) {
+        break;
+      }
+      *newline = '\0';
+      line = &char_buffer[start];
+      start = newline - char_buffer + 1;
+      read_bytes -= newline - line + 1;
+
+      // Ignore the return code, errors are okay.
+      ReadMapFileContent(line, callback);
+    }
+
+    if (read_complete) {
+      close(fd);
+      return true;
+    }
+
+    if (start == 0 && read_bytes == buffer_size - 1) {
+      // The buffer provided is too small to contain this line, give up
+      // and indicate failure.
+      close(fd);
+      return false;
+    }
+
+    // Copy any leftover data to the front  of the buffer.
+    if (start > 0) {
+      if (read_bytes > 0) {
+        memmove(char_buffer, &char_buffer[start], read_bytes);
+      }
+      start = 0;
+    }
+  }
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 562d864..b1bdc08 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -16,9 +16,14 @@
 
 #include <procinfo/process_map.h>
 
+#include <inttypes.h>
+#include <sys/mman.h>
+
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 
 #include <gtest/gtest.h>
 
@@ -63,3 +68,215 @@
   ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
   ASSERT_GT(maps.size(), 0u);
 }
+
+extern "C" void malloc_disable();
+extern "C" void malloc_enable();
+
+struct TestMapInfo {
+  TestMapInfo() = default;
+  TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+              const char* new_name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode) {
+    strcpy(name, new_name);
+  }
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint16_t flags = 0;
+  uint64_t pgoff = 0;
+  ino_t inode = 0;
+  char name[100] = {};
+};
+
+void VerifyReadMapFileAsyncSafe(const char* maps_data,
+                                const std::vector<TestMapInfo>& expected_info) {
+  TemporaryFile tf;
+  ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
+
+  std::vector<TestMapInfo> saved_info(expected_info.size());
+  size_t num_maps = 0;
+
+  auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+                      const char* name) {
+    if (num_maps != saved_info.size()) {
+      TestMapInfo& saved = saved_info[num_maps];
+      saved.start = start;
+      saved.end = end;
+      saved.flags = flags;
+      saved.pgoff = pgoff;
+      saved.inode = inode;
+      strcpy(saved.name, name);
+    }
+    num_maps++;
+  };
+
+  std::vector<char> buffer(64 * 1024);
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  bool parsed =
+      android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
+  ASSERT_EQ(expected_info.size(), num_maps);
+  for (size_t i = 0; i < expected_info.size(); i++) {
+    const TestMapInfo& expected = expected_info[i];
+    const TestMapInfo& saved = saved_info[i];
+    EXPECT_EQ(expected.start, saved.start);
+    EXPECT_EQ(expected.end, saved.end);
+    EXPECT_EQ(expected.flags, saved.flags);
+    EXPECT_EQ(expected.pgoff, saved.pgoff);
+    EXPECT_EQ(expected.inode, saved.inode);
+    EXPECT_STREQ(expected.name, saved.name);
+  }
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_invalid) {
+  std::vector<TestMapInfo> expected_info;
+
+  VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+                             "/lib/fake.so");
+
+  VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
+                             expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+                             "/lib/fake.so");
+
+  VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
+                             expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "");
+
+  VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "");
+  expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so");
+  expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so");
+  expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]");
+
+  std::string map_data =
+      "0a0000-0c0000 rwxp 00000001 00:05 100\n"
+      "0d0000-0e0000 r--p 00000002 00:05 101  /lib/libsomething1.so\n"
+      "0f0000-100000 -w-p 00000003 00:05 102  /lib/libsomething2.so\n"
+      "110000-120000 --xp 00000004 00:05 103  [anon:something or another]\n";
+
+  VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
+  std::vector<TestMapInfo> expected_info;
+  std::string map_data;
+  uint64_t start = 0xa0000;
+  for (size_t i = 0; i < 10000; i++) {
+    map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
+                                            start, start + 0x1000, i, 1000 + i);
+    expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so");
+  }
+
+  VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  char buffer[10];
+  bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  char buffer[10];
+  bool parsed =
+      android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
+  TemporaryFile tf;
+  ASSERT_TRUE(android::base::WriteStringToFd(
+      "0a0000-0c0000 rwxp 00000001 00:05 100    /fake/lib.so\n", tf.fd));
+
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  char buffer[39];
+  bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index d0562d9..0cbcac5 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -165,8 +165,8 @@
   }
 }
 
-int GetInfo(const char* file, uint64_t pc) {
-  Elf elf(Memory::CreateFileMemory(file, pc).release());
+int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
+  Elf elf(Memory::CreateFileMemory(file, offset).release());
   if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
@@ -243,12 +243,14 @@
 }  // namespace unwindstack
 
 int main(int argc, char** argv) {
-  if (argc != 3) {
-    printf("Usage: unwind_reg_info ELF_FILE PC\n");
+  if (argc != 3 && argc != 4) {
+    printf("Usage: unwind_reg_info ELF_FILE PC [OFFSET]\n");
     printf("  ELF_FILE\n");
     printf("    The path to an elf file.\n");
     printf("  PC\n");
     printf("    The pc for which the register information should be obtained.\n");
+    printf("  OFFSET\n");
+    printf("    Use the offset into the ELF file as the beginning of the elf.\n");
     return 1;
   }
 
@@ -270,5 +272,15 @@
     return 1;
   }
 
-  return unwindstack::GetInfo(argv[1], pc);
+  uint64_t offset = 0;
+  if (argc == 4) {
+    char* end;
+    offset = strtoull(argv[3], &end, 16);
+    if (*end != '\0') {
+      printf("Malformed OFFSET value: %s\n", argv[3]);
+      return 1;
+    }
+  }
+
+  return unwindstack::GetInfo(argv[1], offset, pc);
 }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 2fd9f95..82c4fb9 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -56,14 +56,7 @@
       mLogId(elem.mLogId),
       mDropped(elem.mDropped) {
     if (mDropped) {
-        if (elem.isBinary() && elem.mMsg != nullptr) {
-            // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
-            const int len = sizeof(android_event_header_t);
-            mMsg = new char[len];
-            memcpy(mMsg, elem.mMsg, len);
-        } else {
-            mMsg = nullptr;
-        }
+        mTag = elem.getTag();
     } else {
         mMsg = new char[mMsgLen];
         memcpy(mMsg, elem.mMsg, mMsgLen);
@@ -71,31 +64,43 @@
 }
 
 LogBufferElement::~LogBufferElement() {
-    delete[] mMsg;
+    if (!mDropped) {
+        delete[] mMsg;
+    }
 }
 
 uint32_t LogBufferElement::getTag() const {
-    return (isBinary() &&
-            ((mDropped && mMsg != nullptr) ||
-             (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
-               ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
-               : 0;
+    // Binary buffers have no tag.
+    if (!isBinary()) {
+        return 0;
+    }
+
+    // Dropped messages store the tag in place of mMsg.
+    if (mDropped) {
+        return mTag;
+    }
+
+    // For non-dropped messages, we get the tag from the message header itself.
+    if (mMsgLen < sizeof(android_event_header_t)) {
+        return 0;
+    }
+
+    return reinterpret_cast<const android_event_header_t*>(mMsg)->tag;
 }
 
 uint16_t LogBufferElement::setDropped(uint16_t value) {
-    // The tag information is saved in mMsg data, if the tag is non-zero
-    // save only the information needed to get the tag.
-    if (getTag() != 0) {
-        if (mMsgLen > sizeof(android_event_header_t)) {
-            char* truncated_msg = new char[sizeof(android_event_header_t)];
-            memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
-            delete[] mMsg;
-            mMsg = truncated_msg;
-        }  // mMsgLen == sizeof(android_event_header_t), already at minimum.
-    } else {
-        delete[] mMsg;
-        mMsg = nullptr;
+    if (mDropped) {
+        return mDroppedCount = value;
     }
+
+    // The tag information is saved in mMsg data, which is in a union with mTag, used after mDropped
+    // is set to true. Therefore we save the tag value aside, delete mMsg, then set mTag to the tag
+    // value in its place.
+    auto old_tag = getTag();
+    delete[] mMsg;
+    mMsg = nullptr;
+
+    mTag = old_tag;
     mDropped = true;
     return mDroppedCount = value;
 }
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 57b0a95..da4991b 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -41,7 +41,10 @@
     const uint32_t mPid;
     const uint32_t mTid;
     log_time mRealTime;
-    char* mMsg;
+    union {
+        char* mMsg;    // mDropped == false
+        int32_t mTag;  // mDropped == true
+    };
     union {
         const uint16_t mMsgLen;  // mDropped == false
         uint16_t mDroppedCount;  // mDropped == true
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4d34b67..bb36139 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -127,6 +127,10 @@
     mkdir /mnt/expand 0771 system system
     mkdir /mnt/appfuse 0711 root root
 
+    # tmpfs place for BORINGSSL_self_test() to remember whether it has run
+    mkdir /dev/boringssl 0755 root root
+    mkdir /dev/boringssl/selftest 0755 root root
+
     # Storage views to support runtime permissions
     mkdir /mnt/runtime 0700 root root
     mkdir /mnt/runtime/default 0755 root root
@@ -532,6 +536,7 @@
     mkdir /data/misc/profiles/ref 0771 system system
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
+    mkdir /data/misc/installd 0700 root root
 
     mkdir /data/preloads 0775 system system
 
@@ -614,6 +619,9 @@
     mkdir /data/cache/backup_stage 0700 system system
     mkdir /data/cache/backup 0700 system system
 
+    mkdir /data/rollback 0700 system system
+    mkdir /data/rollback-observer 0700 system system
+
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
     parse_apex_configs
@@ -860,6 +868,8 @@
     chmod 0773 /data/misc/trace
     # Give reads to anyone for the window trace folder on debug builds.
     chmod 0775 /data/misc/wmtrace
+
+on init && property:ro.debuggable=1
     start console
 
 service flash_recovery /system/bin/install-recovery.sh
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 2b35819..622de5b 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -315,7 +315,8 @@
         PLOG(ERROR) << "setting RLIMIT_NOFILE failed";
     }
 
-    while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
+    while ((fs_read_atomic_int("/data/misc/installd/layout_version", &fs_version) == -1) ||
+           (fs_version < 3)) {
         LOG(ERROR) << "installd fs upgrade not yet complete; waiting...";
         sleep(1);
     }
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index ca2421b..6668cf3 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -41,7 +41,6 @@
 using android::hardware::health::V2_0::StorageInfo;
 
 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
-const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
 const char* emmc_info_t::emmc_ver_str[9] = {
     "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
 };
@@ -62,10 +61,8 @@
     if (healthService != nullptr) {
         return new health_storage_info_t(healthService);
     }
-    if (FileExists(emmc_info_t::emmc_sysfs) ||
-        FileExists(emmc_info_t::emmc_debugfs)) {
-        return new emmc_info_t;
-    }
+    if (FileExists(emmc_info_t::emmc_sysfs)) return new emmc_info_t;
+
     if (FileExists(ufs_info_t::health_file)) {
         return new ufs_info_t;
     }
@@ -241,8 +238,7 @@
 
 void emmc_info_t::report()
 {
-    if (!report_sysfs() && !report_debugfs())
-        return;
+    if (!report_sysfs()) return;
 
     publish();
 }
@@ -284,54 +280,6 @@
     return true;
 }
 
-namespace {
-
-const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
-/* 2 characters in string for each byte */
-const size_t EXT_CSD_REV_IDX = 192 * 2;
-const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
-
-} // namespace
-
-bool emmc_info_t::report_debugfs()
-{
-    string buffer;
-    uint16_t rev = 0;
-
-    if (!ReadFileToString(emmc_debugfs, &buffer) ||
-        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
-        return false;
-    }
-
-    string str = buffer.substr(EXT_CSD_REV_IDX, 2);
-    if (!ParseUint(str, &rev) ||
-        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
-        return false;
-    }
-
-    version = "emmc ";
-    version += emmc_ver_str[rev];
-
-    str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
-    if (!ParseUint(str, &eol)) {
-        return false;
-    }
-
-    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
-    if (!ParseUint(str, &lifetime_a)) {
-        return false;
-    }
-
-    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
-    if (!ParseUint(str, &lifetime_b)) {
-        return false;
-    }
-
-    return true;
-}
-
 void ufs_info_t::report()
 {
     string buffer;