Merge "remount: Improve messaging when a reboot is needed."
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/base/Android.bp b/base/Android.bp
index 58af2eb..61fbc3d 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -51,6 +51,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "29",
 }
 
 cc_defaults {
@@ -132,6 +133,7 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
+    min_sdk_version: "29",
 }
 
 cc_library_static {
diff --git a/base/file.cpp b/base/file.cpp
index 6321fc6..97cc2b2 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -225,7 +225,7 @@
     content->reserve(sb.st_size);
   }
 
-  char buf[BUFSIZ];
+  char buf[BUFSIZ] __attribute__((__uninitialized__));
   ssize_t n;
   while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
     content->append(buf, n);
diff --git a/base/logging.cpp b/base/logging.cpp
index 6e9c67f..5bd21da 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -260,7 +260,7 @@
   // The kernel's printk buffer is only 1024 bytes.
   // TODO: should we automatically break up long lines into multiple lines?
   // Or we could log but with something like "..." at the end?
-  char buf[1024];
+  char buf[1024] __attribute__((__uninitialized__));
   size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
   if (size > sizeof(buf)) {
     size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
index 78e1e8d..e83ab13 100644
--- a/base/stringprintf.cpp
+++ b/base/stringprintf.cpp
@@ -25,7 +25,7 @@
 
 void StringAppendV(std::string* dst, const char* format, va_list ap) {
   // First try with a small fixed size buffer
-  char space[1024];
+  char space[1024] __attribute__((__uninitialized__));
 
   // It's possible for methods that use a va_list to invalidate
   // the data in it upon use.  The fix is to make a copy
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 9d18a44..bde9d0a 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -69,6 +69,7 @@
         "libdm",
         "libfs_mgr",
         "liblog",
+        "libgsi",
     ],
 
     data: [
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 22a3722..3c8ab42 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -35,6 +35,7 @@
 #include <libdm/loop_control.h>
 #include <libfiemap/fiemap_writer.h>
 #include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
 
 #include "utility.h"
 
@@ -148,7 +149,10 @@
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
     EXPECT_EQ(fptr->size(), gBlockSize);
     EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
-    EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+
+    if (!android::gsi::IsGsiRunning()) {
+        EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+    }
 }
 
 TEST_F(FiemapWriterTest, CheckFileCreated) {
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2f516fa..dc3b985 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -401,8 +401,8 @@
         // untouched to be compatible code that looks for an MBR. Thus we
         // start counting free sectors at sector 1, not 0.
         uint64_t free_area_start = LP_SECTOR_SIZE;
-        if (out.alignment || out.alignment_offset) {
-            free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+        if (out.alignment) {
+            free_area_start = AlignTo(free_area_start, out.alignment);
         } else {
             free_area_start = AlignTo(free_area_start, logical_block_size);
         }
@@ -442,7 +442,7 @@
     // Compute the first free sector, factoring in alignment.
     uint64_t free_area_start = total_reserved;
     if (super.alignment || super.alignment_offset) {
-        free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+        free_area_start = AlignTo(free_area_start, super.alignment);
     } else {
         free_area_start = AlignTo(free_area_start, logical_block_size);
     }
@@ -930,7 +930,7 @@
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
     uint64_t lba = sector * LP_SECTOR_SIZE;
-    uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
+    uint64_t aligned = AlignTo(lba, block_device.alignment);
     return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
 }
 
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 977ebe3..52a3217 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -174,7 +174,7 @@
     ASSERT_NE(exported, nullptr);
     super_device = GetMetadataSuperBlockDevice(*exported.get());
     ASSERT_NE(super_device, nullptr);
-    EXPECT_EQ(super_device->first_logical_sector, 1472);
+    EXPECT_EQ(super_device->first_logical_sector, 1536);
 
     // Alignment offset without alignment doesn't mean anything.
     device_info.alignment = 0;
@@ -190,7 +190,7 @@
     ASSERT_NE(exported, nullptr);
     super_device = GetMetadataSuperBlockDevice(*exported.get());
     ASSERT_NE(super_device, nullptr);
-    EXPECT_EQ(super_device->first_logical_sector, 174);
+    EXPECT_EQ(super_device->first_logical_sector, 168);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -200,7 +200,7 @@
     ASSERT_NE(exported, nullptr);
     super_device = GetMetadataSuperBlockDevice(*exported.get());
     ASSERT_NE(super_device, nullptr);
-    EXPECT_EQ(super_device->first_logical_sector, 160);
+    EXPECT_EQ(super_device->first_logical_sector, 154);
 }
 
 TEST_F(BuilderTest, InternalPartitionAlignment) {
@@ -229,12 +229,12 @@
         EXPECT_EQ(extent.num_sectors, 80);
 
         uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
-        uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+        uint64_t aligned_lba = AlignTo(lba, device_info.alignment);
         EXPECT_EQ(lba, aligned_lba);
     }
 
     // Sanity check one extent.
-    EXPECT_EQ(exported->extents.back().target_data, 3008);
+    EXPECT_EQ(exported->extents.back().target_data, 3072);
 }
 
 TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -652,7 +652,7 @@
     };
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
     ASSERT_NE(builder, nullptr);
-    EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+    EXPECT_EQ(builder->AllocatableSpace(), 467402752);
 
     // Create a partition that spans 3 devices.
     Partition* p = builder->AddPartition("system_a", 0);
@@ -675,17 +675,17 @@
     EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
     EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
     ASSERT_EQ(metadata->extents.size(), 3);
-    EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+    EXPECT_EQ(metadata->extents[0].num_sectors, 522752);
     EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
-    EXPECT_EQ(metadata->extents[0].target_data, 1984);
+    EXPECT_EQ(metadata->extents[0].target_data, 1536);
     EXPECT_EQ(metadata->extents[0].target_source, 0);
-    EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+    EXPECT_EQ(metadata->extents[1].num_sectors, 260608);
     EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
-    EXPECT_EQ(metadata->extents[1].target_data, 1472);
+    EXPECT_EQ(metadata->extents[1].target_data, 1536);
     EXPECT_EQ(metadata->extents[1].target_source, 1);
-    EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+    EXPECT_EQ(metadata->extents[2].num_sectors, 128704);
     EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
-    EXPECT_EQ(metadata->extents[2].target_data, 1472);
+    EXPECT_EQ(metadata->extents[2].target_data, 1536);
     EXPECT_EQ(metadata->extents[2].target_source, 2);
 }
 
@@ -1019,3 +1019,35 @@
     EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
     EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
 }
+
+TEST_F(BuilderTest, AlignFreeRegion) {
+    BlockDeviceInfo super("super", 8_GiB, 786432, 0, 4096);
+    std::vector<BlockDeviceInfo> block_devices = {super};
+
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* p = builder->AddPartition("system", "default", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->AddLinearExtent(p, "super", 64, (super.alignment + 4096) / 512));
+
+    p = builder->AddPartition("vendor", "default", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(p, 2_GiB));
+
+    const auto& extents = p->extents();
+    ASSERT_EQ(extents.size(), 2);
+
+    LinearExtent* e1 = extents[0]->AsLinearExtent();
+    ASSERT_NE(e1, nullptr);
+    LinearExtent* e2 = extents[1]->AsLinearExtent();
+    ASSERT_NE(e2, nullptr);
+
+    // The misaligned partition starting at sector 1544 should not cause any
+    // overlap with previous extents. We should see vendor punch a hole where
+    // "system" is, extending the hole up to the next aligned block.
+    EXPECT_EQ(e1->physical_sector(), 1536);
+    EXPECT_EQ(e1->end_sector(), 1544);
+    EXPECT_EQ(e2->physical_sector(), 3072);
+    EXPECT_EQ(e2->end_sector(), 4197368);
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 99bff6e..6af9d94 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -50,16 +50,7 @@
     // 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);
-
-    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
-        EXPECT_EQ(device_info.alignment_offset, 0);
-    }
 }
 
 TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 0661769..f210eaf 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -78,17 +78,6 @@
     return base + (alignment - remainder);
 }
 
-// Same as the above |AlignTo|, except that |base| is only aligned when added to
-// |alignment_offset|.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
-    uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
-    if (aligned - alignment >= base) {
-        // We overaligned (base < alignment_offset).
-        return aligned - alignment;
-    }
-    return aligned;
-}
-
 // Update names from C++ strings.
 bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
 bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index cac3989..b64861d 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -64,9 +64,9 @@
     EXPECT_EQ(AlignTo(555, 1024), 1024);
     EXPECT_EQ(AlignTo(555, 1000), 1000);
     EXPECT_EQ(AlignTo(0, 1024), 0);
-    EXPECT_EQ(AlignTo(54, 32, 30), 62);
-    EXPECT_EQ(AlignTo(32, 32, 30), 62);
-    EXPECT_EQ(AlignTo(17, 32, 30), 30);
+    EXPECT_EQ(AlignTo(54, 32), 64);
+    EXPECT_EQ(AlignTo(32, 32), 32);
+    EXPECT_EQ(AlignTo(17, 32), 32);
 }
 
 TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 384595d..40da1bc 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -100,6 +100,7 @@
 
 cc_library_static {
     name: "libsnapshot_init",
+    native_coverage : true,
     defaults: ["libsnapshot_defaults"],
     srcs: [":libsnapshot_sources"],
     recovery_available: true,
@@ -160,23 +161,23 @@
         "snapshot_test.cpp",
     ],
     shared_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
         "libbinder",
         "libcrypto",
         "libhidlbase",
         "libprotobuf-cpp-lite",
-        "libsparse",
         "libutils",
         "libz",
     ],
     static_libs: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
         "libfs_mgr",
         "libgsi",
         "libgmock",
         "liblp",
         "libsnapshot",
         "libsnapshot_test_helpers",
+        "libsparse",
     ],
     header_libs: [
         "libstorage_literals_headers",
@@ -244,3 +245,43 @@
     ],
     gtest: false,
 }
+
+cc_fuzz {
+    name: "libsnapshot_fuzzer",
+
+    // TODO(b/154633114): make host supported.
+    // host_supported: true,
+
+    native_coverage : true,
+    srcs: [
+        "snapshot_fuzz.cpp",
+        "snapshot_fuzz_utils.cpp",
+        "fuzz_utils.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libcrypto_static",
+        "libcutils",
+        "libfs_mgr",
+        "libgtest", // from libsnapshot_test_helpers
+        "libgmock", // from libsnapshot_test_helpers
+        "liblog",
+        "liblp",
+        "libsnapshot_init", // don't use binder or hwbinder
+        "libsnapshot_test_helpers",
+        "libprotobuf-cpp-lite",
+        "update_metadata-protos",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+
+    fuzz_config: {
+        cc: ["android-virtual-ab+bugs@google.com"],
+        componentid: 30545,
+        hotlists: ["1646452"],
+        fuzz_on_haiku_host: false,
+        // TODO(b/154633114): set to true to run this automatically.
+        fuzz_on_haiku_device: false,
+    },
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
new file mode 100755
index 0000000..64d8224
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+PROJECT_PATH=system/core/fs_mgr/libsnapshot
+FUZZ_TARGET=libsnapshot_fuzzer
+TARGET_ARCH=$(get_build_var TARGET_ARCH)
+FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}
+DEVICE_CORPSE_DIR=/data/local/tmp/${FUZZ_TARGET}
+DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
+HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
+GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
+
+build_normal() (
+    pushd $(gettop)
+    NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" COVERAGE_PATHS="" m ${FUZZ_TARGET}
+    ret=$?
+    popd
+    return ${ret}
+)
+
+build_cov() {
+    pushd $(gettop)
+    ret=$?
+    NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
+    popd
+    return ${ret}
+}
+
+prepare_device() {
+    adb root && adb remount &&
+    adb shell mkdir -p ${DEVICE_CORPSE_DIR} &&
+    adb shell rm -rf ${DEVICE_GCOV_DIR} &&
+    adb shell mkdir -p ${DEVICE_GCOV_DIR}
+}
+
+push_binary() {
+    adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY}
+}
+
+prepare_host() {
+    which lcov || {
+        echo "please run:";
+        echo "   sudo apt-get install lcov ";
+        return 1;
+    }
+    rm -rf ${HOST_SCRATCH_DIR} &&
+    mkdir -p ${HOST_SCRATCH_DIR}
+}
+
+# run_snapshot_fuzz -runs=10000
+generate_corpse() {
+    [[ "$@" ]] || { echo "run with -runs=X"; return 1; }
+
+    prepare_device &&
+    build_normal &&
+    push_binary &&
+    adb shell ${FUZZ_BINARY} "$@" ${DEVICE_CORPSE_DIR}
+}
+
+run_snapshot_fuzz() {
+    prepare_device &&
+    build_cov &&
+    push_binary &&
+    adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
+        ${FUZZ_BINARY} \
+        -runs=0 \
+        ${DEVICE_CORPSE_DIR}
+}
+
+show_fuzz_result() {
+    prepare_host &&
+    unzip -o -j -d ${HOST_SCRATCH_DIR} ${ANDROID_PRODUCT_OUT}/coverage/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}.zip &&
+    adb shell find ${DEVICE_GCOV_DIR} -type f | xargs -I {} adb pull {} ${HOST_SCRATCH_DIR} &&
+    ls ${HOST_SCRATCH_DIR} &&
+    cat > ${GCOV_TOOL} <<< '
+#!/bin/bash
+exec llvm-cov gcov "$@"
+' &&
+    chmod +x ${GCOV_TOOL} &&
+    lcov --directory ${HOST_SCRATCH_DIR} --base-directory $(gettop) --gcov-tool ${GCOV_TOOL} --capture -o ${HOST_SCRATCH_DIR}/report.cov &&
+    genhtml ${HOST_SCRATCH_DIR}/report.cov -o ${HOST_SCRATCH_DIR}/html &&
+    echo file://$(realpath ${HOST_SCRATCH_DIR}/html/index.html)
+}
+
+# run_snapshot_fuzz -runs=10000
+run_snapshot_fuzz_all() {
+    generate_corpse "$@" &&
+    run_snapshot_fuzz &&
+    show_fuzz_result
+}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp
new file mode 100644
index 0000000..509eb1b
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 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 "fuzz_utils.h"
+
+#include <android-base/logging.h>
+
+namespace android::fuzz {
+
+void CheckInternal(bool value, std::string_view msg) {
+    CHECK(value) << msg;
+}
+
+}  // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
new file mode 100644
index 0000000..4e14b9c
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -0,0 +1,267 @@
+// Copyright (C) 2020 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 <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+// Generic classes for fuzzing a collection of APIs.
+
+namespace android::fuzz {
+
+// My custom boolean type -- to avoid conflict with (u)int8_t and char.
+struct Bool {
+    bool value;
+    operator bool() const { return value; }
+};
+
+// Helper for FuzzData.
+// A wrapper over an optional const object T. The buffer is maintained elsewhere.
+template <typename T>
+class Optional {
+  public:
+    Optional(const T* ptr) : ptr_(ptr) {}
+    const T& operator*() const { return *ptr_; }
+    const T& value() const { return *ptr_; }
+    bool has_value() const { return ptr_; }
+
+  private:
+    const T* ptr_;
+};
+
+// Helper for FuzzData.
+// A wrapper over an optional boolean. The boolean is owned by this object.
+template <>
+class Optional<Bool> {
+  public:
+    Optional(std::optional<Bool>&& val) : val_(std::move(val)) {}
+    const Bool& operator*() const { return *val_; }
+    const Bool& value() const { return val_.value(); }
+    bool has_value() const { return val_.has_value(); }
+
+  private:
+    std::optional<Bool> val_;
+};
+
+// Helper for FuzzData.
+// A view on a raw data buffer. Client is responsible for maintaining the lifetime of the data
+// buffer.
+class DataView {
+  public:
+    DataView(const uint8_t* data, uint64_t size) : data_(data), size_(size) {}
+    DataView(const void* data, uint64_t size) : DataView(static_cast<const uint8_t*>(data), size) {}
+    inline uint64_t size() const { return size_; }
+    inline const uint8_t* data() const { return data_; }
+    inline bool CanConsume(uint64_t size) { return size_ >= size; }
+    // Consume the first |size| bytes from |this| and return a DataView object that represents
+    // the consumed data. Data pointer in |this| is incremented by |size| bytes.
+    // If not enough bytes, return nullopt.
+    std::optional<DataView> Consume(uint64_t size) {
+        if (!CanConsume(size)) return std::nullopt;
+        DataView ret(data_, size);
+        size_ -= size;
+        data_ += size;
+        return ret;
+    }
+
+  private:
+    const uint8_t* data_;
+    uint64_t size_;
+};
+
+// A view on the fuzz data. Provides APIs to consume typed objects.
+class FuzzData : public DataView {
+  public:
+    // Inherit constructors.
+    using DataView::DataView;
+    // Consume a data object T and return the pointer (into the buffer). No copy is done.
+    // If not enough bytes, return nullptr.
+    template <typename T>
+    inline Optional<T> Consume() {
+        auto data_view = DataView::Consume(sizeof(T));
+        if (!data_view.has_value()) return nullptr;
+        return reinterpret_cast<const T*>(data_view->data());
+    }
+    // To provide enough entropy for booleans, they are consumed bit by bit.
+    // Hence, the returned value is not indexed into the buffer. See Optional<Bool>.
+    template <>
+    Optional<Bool> Consume<Bool>() {
+        if (!boolean_buffer_.has_value() || boolean_bit_offset_ >= sizeof(*boolean_buffer_)) {
+            boolean_buffer_ = Consume<uint8_t>();
+            boolean_bit_offset_ = 0;
+        }
+        if (!boolean_buffer_.has_value()) {
+            return Optional<Bool>(std::nullopt);
+        }
+        const auto& byte = *boolean_buffer_;
+        bool ret = (byte >> boolean_bit_offset_) & 0x1;
+        boolean_bit_offset_++;
+        return Optional<Bool>(Bool{ret});
+    }
+
+  private:
+    // Separate buffer for booleans.
+    Optional<uint8_t> boolean_buffer_ = nullptr;
+    uint8_t boolean_bit_offset_ = 0;
+};
+
+enum class CallResult {
+    SUCCESS,
+    NOT_ENOUGH_DATA,
+};
+
+inline bool AllArgsHasValue() {
+    return true;
+}
+template <typename T>
+inline bool AllArgsHasValue(const Optional<T>& t) {
+    return t.has_value();
+}
+template <typename First, typename... Remaining>
+inline bool AllArgsHasValue(const Optional<First>& first, const Optional<Remaining>&... remaining) {
+    return first.has_value() && AllArgsHasValue(remaining...);
+}
+
+// Base class of FuzzFunction.
+class FuzzFunctionBase {
+  public:
+    virtual ~FuzzFunctionBase() = default;
+    virtual CallResult Call(FuzzData* fuzz_data) const = 0;
+};
+
+template <typename T>
+class FuzzFunction;  // undefined
+
+// A wrapper over a fuzzed function.
+template <typename R, typename... Args>
+class FuzzFunction<R(Args...)> : public FuzzFunctionBase {
+  public:
+    using Function = std::function<R(Args...)>;
+    FuzzFunction(Function&& function) : function_(std::move(function)) {}
+    // Eat necessary data in |fuzz_data| and invoke the function.
+    CallResult Call(FuzzData* fuzz_data) const override {
+        return CallWithOptionalArgs(fuzz_data->Consume<std::remove_reference_t<Args>>()...);
+    }
+
+  private:
+    Function function_;
+
+    CallResult CallWithOptionalArgs(const Optional<std::remove_reference_t<Args>>&... args) const {
+        if (!AllArgsHasValue(args...)) {
+            return CallResult::NOT_ENOUGH_DATA;
+        }
+        (void)function_(args.value()...);  // ignore returned value
+        return CallResult::SUCCESS;
+    }
+};
+
+// CHECK(value) << msg
+void CheckInternal(bool value, std::string_view msg);
+
+// A collection of FuzzFunction's.
+// FunctionsSizeType must be an integral type where
+// functions_.size() <= std::numeric_limits<FunctionSizeType>::max().
+template <typename FunctionsSizeType>
+class FuzzFunctions {
+  public:
+    // Subclass should override this to register functions via AddFunction.
+    FuzzFunctions() = default;
+    virtual ~FuzzFunctions() = default;
+    // Eat some amount of data in |fuzz_data| and call one of the |functions_|.
+    CallResult CallOne(FuzzData* fuzz_data) const {
+        auto opt_number = fuzz_data->Consume<FunctionsSizeType>();
+        if (!opt_number.has_value()) {
+            return CallResult::NOT_ENOUGH_DATA;
+        }
+        auto function_index = opt_number.value() % functions_.size();
+        return functions_[function_index]->Call(fuzz_data);
+    }
+
+  private:
+    template <typename R, typename... Args>
+    struct FunctionTraits {
+        using Function = std::function<R(Args...)>;
+    };
+
+  public:
+    // There are no deduction guide from lambda to std::function, so the
+    // signature of the lambda must be specified in the template argument.
+    // FuzzFunctions provide the following 3 ways to specify the signature of
+    // the lambda:
+
+    // AddFunction<R(Args...)>, e.g. AddFunction<ReturnType(ArgType, ArgType)>
+    template <typename T>
+    void AddFunction(std::function<T>&& func) {
+        functions_.push_back(std::make_unique<FuzzFunction<T>>(std::move(func)));
+    }
+
+    // AddFunction<R, Args...>, e.g. AddFunction<ReturnType, ArgType, ArgType>
+    template <typename R, typename... Args>
+    void AddFunction(typename FunctionTraits<R, Args...>::Function&& func) {
+        functions_.push_back(std::make_unique<FuzzFunction<R(Args...)>>(std::move(func)));
+    }
+
+    // AddFunction<ArgType...>. Equivalent to AddFunction<void, Args...>
+    template <typename... Args>
+    void AddFunction(typename FunctionTraits<void, Args...>::Function&& func) {
+        functions_.push_back(std::make_unique<FuzzFunction<void(Args...)>>(std::move(func)));
+    }
+
+    // Use |fuzz_data| as a guide to call |functions_| until |fuzz_data| is
+    // depleted. Return
+    void DepleteData(FuzzData* fuzz_data) const {
+        CallResult result;
+        while ((result = CallOne(fuzz_data)) == CallResult::SUCCESS)
+            ;
+        CheckInternal(result == CallResult::NOT_ENOUGH_DATA,
+                      "result is " + std::to_string(static_cast<int>(result)));
+    }
+
+  protected:
+    // Helper for subclass to check that size of |functions_| is actually within
+    // SizeType. Should be called after all functions are registered.
+    void CheckFunctionsSize() const {
+        CheckInternal(functions_.size() <= std::numeric_limits<FunctionsSizeType>::max(),
+                      "Need to extend number of bits for function count; there are " +
+                              std::to_string(functions_.size()) + " functions now.");
+    }
+
+  private:
+    std::vector<std::unique_ptr<FuzzFunctionBase>> functions_;
+};
+
+// An object whose APIs are being fuzzed.
+template <typename T, typename FunctionsSizeType>
+class FuzzObject : public FuzzFunctions<FunctionsSizeType> {
+  public:
+    // Not thread-safe; client is responsible for ensuring only one thread calls DepleteData.
+    void DepleteData(T* obj, FuzzData* fuzz_data) {
+        obj_ = obj;
+        FuzzFunctions<FunctionsSizeType>::DepleteData(fuzz_data);
+        obj_ = nullptr;
+    }
+
+  protected:
+    // Helper for subclass to get the module under test in the added functions.
+    T* get() const {
+        CheckInternal(obj_ != nullptr, "No module under test is found.");
+        return obj_;
+    }
+
+  private:
+    T* obj_ = nullptr;
+};
+
+}  // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index fff667e..8081866 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -318,6 +318,7 @@
     friend class SnapshotUpdateTest;
     friend class FlashAfterUpdateTest;
     friend class LockTestConsumer;
+    friend class SnapshotFuzzEnv;
     friend struct AutoDeleteCowImage;
     friend struct AutoDeleteSnapshot;
     friend struct PartitionCowCreator;
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
new file mode 100644
index 0000000..12a0531
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -0,0 +1,141 @@
+// Copyright (C) 2020 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 <stddef.h>
+#include <stdint.h>
+#include <sysexits.h>
+
+#include <functional>
+#include <sstream>
+#include <tuple>
+
+#include <android-base/logging.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fuzz_utils.h"
+#include "snapshot_fuzz_utils.h"
+
+using android::base::LogId;
+using android::base::LogSeverity;
+using android::base::SetLogger;
+using android::base::StderrLogger;
+using android::base::StdioLogger;
+using android::fuzz::Bool;
+using android::fuzz::FuzzData;
+using android::fuzz::FuzzObject;
+using android::snapshot::SnapshotFuzzEnv;
+using android::snapshot::SnapshotManagerFuzzData;
+
+// Avoid linking to libgsi since it needs disk I/O.
+namespace android::gsi {
+bool IsGsiRunning() {
+    LOG(FATAL) << "Called IsGsiRunning";
+    __builtin_unreachable();
+}
+std::string GetDsuSlot(const std::string& install_dir) {
+    LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")";
+    __builtin_unreachable();
+}
+}  // namespace android::gsi
+
+namespace android::snapshot {
+
+class FuzzSnapshotManager : public FuzzObject<ISnapshotManager, uint8_t> {
+  public:
+    FuzzSnapshotManager();
+};
+
+FuzzSnapshotManager::FuzzSnapshotManager() {
+    AddFunction([this]() { (void)get()->BeginUpdate(); });
+    AddFunction([this]() { (void)get()->CancelUpdate(); });
+    AddFunction<Bool>([this](Bool wipe) { (void)get()->FinishedSnapshotWrites(wipe); });
+    AddFunction([this]() { (void)get()->InitiateMerge(); });
+    AddFunction<Bool, Bool>([this](auto has_before_cancel, auto fail_before_cancel) {
+        std::function<bool()> before_cancel;
+        if (has_before_cancel) {
+            before_cancel = [=]() { return fail_before_cancel; };
+        }
+        (void)get()->ProcessUpdateState({}, before_cancel);
+    });
+    AddFunction<Bool>([this](auto has_progress_arg) {
+        double progress;
+        (void)get()->GetUpdateState(has_progress_arg ? &progress : nullptr);
+    });
+    // TODO add CreateUpdateSnapshots according to proto
+    // TODO add MapUpdateSnapshot
+    // TODO add UnmapUpdateSnapshot using names from the dictionary
+    AddFunction([this]() { (void)get()->NeedSnapshotsInFirstStageMount(); });
+    // TODO add CreateLogicalAndSnapshotPartitions
+    AddFunction<Bool>([this](const Bool& has_callback) {
+        std::function<void()> callback;
+        if (has_callback) {
+            callback = []() {};
+        }
+        (void)get()->HandleImminentDataWipe(callback);
+    });
+    AddFunction([this]() { (void)get()->RecoveryCreateSnapshotDevices(); });
+    // TODO add RecoveryCreateSnapshotDevices with metadata_device arg
+    AddFunction([this]() {
+        std::stringstream ss;
+        (void)get()->Dump(ss);
+    });
+    AddFunction([this]() { (void)get()->EnsureMetadataMounted(); });
+    AddFunction([this]() { (void)get()->GetSnapshotMergeStatsInstance(); });
+
+    CheckFunctionsSize();
+}
+
+// During global init, log all messages to stdio. This is only done once.
+int AllowLoggingDuringGlobalInit() {
+    SetLogger(&StdioLogger);
+    return 0;
+}
+
+// Only log fatal messages during tests.
+void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file,
+                     unsigned int line, const char* message) {
+    if (severity == LogSeverity::FATAL) {
+        StderrLogger(logid, severity, tag, file, line, message);
+    }
+}
+// Stop logging (except fatal messages) after global initialization. This is only done once.
+int StopLoggingAfterGlobalInit() {
+    SetLogger(&FatalOnlyLogger);
+    return 0;
+}
+
+}  // namespace android::snapshot
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    using namespace android::snapshot;
+
+    [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
+    static SnapshotFuzzEnv env;
+    static FuzzSnapshotManager fuzz_snapshot_manager;
+    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+
+    CHECK(env.InitOk());
+    FuzzData fuzz_data(data, size);
+
+    auto snapshot_manager_data = fuzz_data.Consume<SnapshotManagerFuzzData>();
+    if (!snapshot_manager_data.has_value()) {
+        return 0;
+    }
+    auto snapshot_manager = env.CreateSnapshotManager(snapshot_manager_data.value());
+    CHECK(snapshot_manager);
+
+    fuzz_snapshot_manager.DepleteData(snapshot_manager.get(), &fuzz_data);
+
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
new file mode 100644
index 0000000..d5e3e96
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -0,0 +1,227 @@
+// Copyright (C) 2020 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 <ftw.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sysexits.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
+
+#include "snapshot_fuzz_utils.h"
+
+using namespace android::storage_literals;
+using namespace std::string_literals;
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+using android::fiemap::IImageManager;
+using android::fiemap::ImageManager;
+
+static const char MNT_DIR[] = "/mnt";
+static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
+static const auto SUPER_IMAGE_SIZE = 16_MiB;
+static const auto FAKE_ROOT_SIZE = 64_MiB;
+
+namespace android::snapshot {
+
+bool Mkdir(const std::string& path) {
+    if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) {
+        PLOG(ERROR) << "Cannot create " << path;
+        return false;
+    }
+    return true;
+}
+
+bool RmdirRecursive(const std::string& path) {
+    auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+        switch (file_type) {
+            case FTW_D:
+            case FTW_DP:
+            case FTW_DNR:
+                if (rmdir(child) == -1) {
+                    PLOG(ERROR) << "rmdir " << child;
+                    return -1;
+                }
+                return 0;
+            case FTW_NS:
+            default:
+                if (rmdir(child) != -1) break;
+                [[fallthrough]];
+            case FTW_F:
+            case FTW_SL:
+            case FTW_SLN:
+                if (unlink(child) == -1) {
+                    PLOG(ERROR) << "unlink " << child;
+                    return -1;
+                }
+                return 0;
+        }
+        return 0;
+    };
+
+    return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
+}
+
+class AutoDeleteDir : public AutoDevice {
+  public:
+    static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
+        if (!Mkdir(path)) {
+            return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(""));
+        }
+        return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(path));
+    }
+    ~AutoDeleteDir() {
+        if (!HasDevice()) return;
+        if (rmdir(name_.c_str()) == -1) {
+            PLOG(ERROR) << "Cannot remove " << name_;
+        }
+    }
+
+  private:
+    AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmount : public AutoDevice {
+  public:
+    static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
+        if (mount("tmpfs", path.c_str(), "tmpfs", 0,
+                  (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
+            PLOG(ERROR) << "Cannot mount " << path;
+            return std::unique_ptr<AutoUnmount>(new AutoUnmount(""));
+        }
+        return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
+    }
+    ~AutoUnmount() {
+        if (!HasDevice()) return;
+        if (umount(name_.c_str()) == -1) {
+            PLOG(ERROR) << "Cannot umount " << name_;
+        }
+    }
+
+  private:
+    AutoUnmount(const std::string& path) : AutoDevice(path) {}
+};
+
+// A directory on tmpfs. Upon destruct, it is unmounted and deleted.
+class AutoMemBasedDir : public AutoDevice {
+  public:
+    static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
+        auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
+        ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
+        if (!ret->auto_delete_mount_dir_->HasDevice()) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
+        ret->auto_umount_mount_point_ = AutoUnmount::New(ret->mount_path(), size);
+        if (!ret->auto_umount_mount_point_->HasDevice()) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
+        // path() does not need to be deleted upon destruction, hence it is not wrapped with
+        // AutoDeleteDir.
+        if (!Mkdir(ret->path())) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
+        return ret;
+    }
+    // Return the scratch directory.
+    std::string path() const {
+        CHECK(HasDevice());
+        return mount_path() + "/root";
+    }
+    // Delete all contents in path() and start over. path() itself is re-created.
+    bool SoftReset() { return RmdirRecursive(path()) && Mkdir(path()); }
+
+  private:
+    AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
+    std::string mount_path() const {
+        CHECK(HasDevice());
+        return MNT_DIR + "/"s + name_;
+    }
+    std::unique_ptr<AutoDeleteDir> auto_delete_mount_dir_;
+    std::unique_ptr<AutoUnmount> auto_umount_mount_point_;
+};
+
+SnapshotFuzzEnv::SnapshotFuzzEnv() {
+    fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
+}
+
+SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
+
+bool SnapshotFuzzEnv::InitOk() const {
+    if (fake_root_ == nullptr || !fake_root_->HasDevice()) return false;
+    return true;
+}
+
+bool SnapshotFuzzEnv::SoftReset() {
+    return fake_root_->SoftReset();
+}
+
+std::unique_ptr<IImageManager> SnapshotFuzzEnv::CreateFakeImageManager(
+        const std::string& fake_root) {
+    auto images_dir = fake_root + "/images";
+    auto metadata_dir = images_dir + "/metadata";
+    auto data_dir = images_dir + "/data";
+
+    if (!Mkdir(images_dir) || !Mkdir(metadata_dir) || !Mkdir(data_dir)) {
+        return nullptr;
+    }
+    return ImageManager::Open(metadata_dir, data_dir);
+}
+
+std::unique_ptr<TestPartitionOpener> SnapshotFuzzEnv::CreatePartitionOpener(
+        const std::string& fake_root) {
+    auto fake_super = fake_root + "/super.img";
+    std::string zeros(SUPER_IMAGE_SIZE, '\0');
+
+    if (!WriteStringToFile(zeros, fake_super)) {
+        PLOG(ERROR) << "Cannot write zeros to " << fake_super;
+        return nullptr;
+    }
+
+    return std::make_unique<TestPartitionOpener>(fake_super);
+}
+
+std::string SnapshotFuzzEnv::root() const {
+    CHECK(InitOk());
+    return fake_root_->path();
+}
+
+std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CreateSnapshotManager(
+        const SnapshotManagerFuzzData& data) {
+    // TODO(b/154633114): create valid super partition according to fuzz data
+    auto partition_opener = CreatePartitionOpener(root());
+    if (partition_opener == nullptr) return nullptr;
+    auto metadata_dir = root() + "/snapshot_metadata";
+    if (!Mkdir(metadata_dir)) return nullptr;
+
+    auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data,
+                                                  std::move(partition_opener), metadata_dir);
+    auto snapshot = SnapshotManager::New(device_info /* takes ownership */);
+    snapshot->images_ = CreateFakeImageManager(root());
+    snapshot->has_local_image_manager_ = data.is_local_image_manager;
+
+    return snapshot;
+}
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
new file mode 100644
index 0000000..32910a9
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -0,0 +1,115 @@
+// Copyright (C) 2020 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 <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <libfiemap/image_manager.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/test_helpers.h>
+
+// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
+// by SnapshotManager.
+
+namespace android::snapshot {
+
+// Controls the behavior of IDeviceInfo.
+typedef struct SnapshotFuzzDeviceInfoData {
+    bool slot_suffix_is_a : 1;
+    bool is_overlayfs_setup : 1;
+    bool allow_set_boot_control_merge_status : 1;
+    bool allow_set_slot_as_unbootable : 1;
+    bool is_recovery : 1;
+} __attribute__((packed)) SnapshotFuzzDeviceInfoData;
+
+// Controls the behavior of the test SnapshotManager.
+typedef struct SnapshotManagerFuzzData {
+    SnapshotFuzzDeviceInfoData device_info_data;
+    bool is_local_image_manager : 1;
+} __attribute__((packed)) SnapshotManagerFuzzData;
+
+class AutoMemBasedDir;
+
+// Prepare test environment. This has a heavy overhead and should be done once.
+class SnapshotFuzzEnv {
+  public:
+    // Check if test should run at all.
+    static bool ShouldSkipTest();
+
+    // Initialize the environment.
+    SnapshotFuzzEnv();
+    ~SnapshotFuzzEnv();
+
+    // Check if environment is initialized properly.
+    bool InitOk() const;
+
+    // A scratch directory for the test to play around with. The scratch directory
+    // is backed by tmpfs. SoftReset() clears the directory.
+    std::string root() const;
+
+    // Soft reset part of the environment before running the next test.
+    bool SoftReset();
+
+    // Create a snapshot manager for this test run.
+    // Client is responsible for maintaining the lifetime of |data| over the life time of
+    // ISnapshotManager.
+    std::unique_ptr<ISnapshotManager> CreateSnapshotManager(const SnapshotManagerFuzzData& data);
+
+  private:
+    std::unique_ptr<AutoMemBasedDir> fake_root_;
+
+    static std::unique_ptr<android::fiemap::IImageManager> CreateFakeImageManager(
+            const std::string& fake_root);
+    static std::unique_ptr<TestPartitionOpener> CreatePartitionOpener(const std::string& fake_root);
+};
+
+class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
+  public:
+    // Client is responsible for maintaining the lifetime of |data|.
+    SnapshotFuzzDeviceInfo(const SnapshotFuzzDeviceInfoData& data,
+                           std::unique_ptr<TestPartitionOpener>&& partition_opener,
+                           const std::string& metadata_dir)
+        : data_(data),
+          partition_opener_(std::move(partition_opener)),
+          metadata_dir_(metadata_dir) {}
+
+    // Following APIs are mocked.
+    std::string GetGsidDir() const override { return "fuzz_ota"; }
+    std::string GetMetadataDir() const override { return metadata_dir_; }
+    std::string GetSuperDevice(uint32_t) const override {
+        // TestPartitionOpener can recognize this.
+        return "super";
+    }
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
+        return *partition_opener_;
+    }
+
+    // Following APIs are fuzzed.
+    std::string GetSlotSuffix() const override { return data_.slot_suffix_is_a ? "_a" : "_b"; }
+    std::string GetOtherSlotSuffix() const override { return data_.slot_suffix_is_a ? "_b" : "_a"; }
+    bool IsOverlayfsSetup() const override { return data_.is_overlayfs_setup; }
+    bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
+        return data_.allow_set_boot_control_merge_status;
+    }
+    bool SetSlotAsUnbootable(unsigned int) override { return data_.allow_set_slot_as_unbootable; }
+    bool IsRecovery() const override { return data_.is_recovery; }
+
+  private:
+    SnapshotFuzzDeviceInfoData data_;
+    std::unique_ptr<TestPartitionOpener> partition_opener_;
+    std::string metadata_dir_;
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b036606..f82a602 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -212,8 +212,8 @@
         return AssertionFailure() << strerror(errno);
     }
     bsize_ = buf.f_bsize;
-    free_space_ = buf.f_bsize * buf.f_bfree;
-    available_space_ = buf.f_bsize * buf.f_bavail;
+    free_space_ = bsize_ * buf.f_bfree;
+    available_space_ = bsize_ * buf.f_bavail;
     return AssertionSuccess();
 }
 
diff --git a/init/README.md b/init/README.md
index 13f1bac..726c0cc 100644
--- a/init/README.md
+++ b/init/README.md
@@ -322,6 +322,10 @@
   This is mutually exclusive with the console option, which additionally connects stdin to the
   given console.
 
+`task_profiles <profile> [ <profile>\* ]`
+> Set task profiles for the process when it forks. This is designed to replace the use of
+  writepid option for moving a process into a cgroup.
+
 `timeout_period <seconds>`
 > Provide a timeout after which point the service will be killed. The oneshot keyword is respected
   here, so oneshot services do not automatically restart, however all other services will.
@@ -356,6 +360,8 @@
   cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
   system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
   '/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
+  The use of this option for moving a process into a cgroup is obsolete. Please
+  use task_profiles option instead.
 
 
 Triggers
diff --git a/init/init.cpp b/init/init.cpp
index a9d6301..3f8f628 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -76,6 +76,7 @@
 #include "service.h"
 #include "service_parser.h"
 #include "sigchld_handler.h"
+#include "subcontext.h"
 #include "system/core/init/property_service.pb.h"
 #include "util.h"
 
@@ -100,8 +101,6 @@
 static int signal_fd = -1;
 static int property_fd = -1;
 
-static std::unique_ptr<Subcontext> subcontext;
-
 struct PendingControlMessage {
     std::string message;
     std::string name;
@@ -216,16 +215,6 @@
     prop_waiter_state.ResetWaitForProp();
 }
 
-static void UnwindMainThreadStack() {
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
-    if (!backtrace->Unwind(0)) {
-        LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
-    }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-        LOG(ERROR) << backtrace->FormatFrameData(i);
-    }
-}
-
 static class ShutdownState {
   public:
     void TriggerShutdown(const std::string& command) {
@@ -243,13 +232,13 @@
     std::optional<std::string> CheckShutdown() {
         auto lock = std::lock_guard{shutdown_command_lock_};
         if (do_shutdown_ && !IsShuttingDown()) {
-            do_shutdown_ = false;
             return shutdown_command_;
         }
         return {};
     }
 
     bool do_shutdown() const { return do_shutdown_; }
+    void set_do_shutdown(bool value) { do_shutdown_ = value; }
 
   private:
     std::mutex shutdown_command_lock_;
@@ -257,16 +246,28 @@
     bool do_shutdown_ = false;
 } shutdown_state;
 
+static void UnwindMainThreadStack() {
+    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
+    if (!backtrace->Unwind(0)) {
+        LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+    }
+    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+        LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+    }
+}
+
 void DebugRebootLogging() {
-    LOG(INFO) << "do_shutdown: " << shutdown_state.do_shutdown()
+    LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
               << " IsShuttingDown: " << IsShuttingDown();
     if (shutdown_state.do_shutdown()) {
         LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
         UnwindMainThreadStack();
+        DumpShutdownDebugInformation();
     }
     if (IsShuttingDown()) {
         LOG(ERROR) << "sys.powerctl set while init is already shutting down";
         UnwindMainThreadStack();
+        DumpShutdownDebugInformation();
     }
 }
 
@@ -279,9 +280,8 @@
     Parser parser;
 
     parser.AddSectionParser("service", std::make_unique<ServiceParser>(
-                                               &service_list, subcontext.get(), std::nullopt));
-    parser.AddSectionParser("on",
-                            std::make_unique<ActionParser>(&action_manager, subcontext.get()));
+                                               &service_list, GetSubcontext(), std::nullopt));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     return parser;
@@ -291,9 +291,9 @@
 Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
     Parser parser;
 
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, subcontext.get(),
-                                                            std::nullopt, from_apex));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
+                                                       from_apex));
     return parser;
 }
 
@@ -721,7 +721,7 @@
     trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
 
     SetStdioToDevNull(argv);
-    InitKernelLogging(argv);
+    InitSecondStageLogging(argv);
     LOG(INFO) << "init second stage started!";
 
     // Init should not crash because of a dependence on any other process, therefore we ignore
@@ -809,7 +809,7 @@
         PLOG(FATAL) << "SetupMountNamespaces failed";
     }
 
-    subcontext = InitializeSubcontext();
+    InitializeSubcontext();
 
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sm = ServiceList::GetInstance();
@@ -874,6 +874,7 @@
             LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                       << "' Calling HandlePowerctlMessage()";
             HandlePowerctlMessage(*shutdown_command);
+            shutdown_state.set_do_shutdown(false);
         }
 
         if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
diff --git a/init/init_test.cpp b/init/init_test.cpp
index caf3e03..07b4724 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -239,6 +239,28 @@
     EXPECT_EQ(6, num_executed);
 }
 
+TEST(init, RejectsCriticalAndOneshotService) {
+    std::string init_script =
+            R"init(
+service A something
+  class first
+  critical
+  oneshot
+)init";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+    ServiceList service_list;
+    Parser parser;
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+    ASSERT_TRUE(parser.ParseConfig(tf.path));
+    ASSERT_EQ(1u, parser.parse_error_count());
+}
+
 }  // namespace init
 }  // namespace android
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 72f0450..e89f74a 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -758,20 +758,24 @@
 
 static Result<void> DoUserspaceReboot() {
     LOG(INFO) << "Userspace reboot initiated";
-    auto guard = android::base::make_scope_guard([] {
+    // An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
+    std::string sub_reason = "";
+    auto guard = android::base::make_scope_guard([&sub_reason] {
         // Leave shutdown so that we can handle a full reboot.
         LeaveShutdown();
-        trigger_shutdown("reboot,userspace_failed,shutdown_aborted");
+        trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
     });
     // Triggering userspace-reboot-requested will result in a bunch of setprop
     // actions. We should make sure, that all of them are propagated before
     // proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
     // property is not perfect, but it should do the trick.
     if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+        sub_reason = "setprop";
         return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
     }
     EnterShutdown();
     if (!SetProperty("sys.powerctl", "")) {
+        sub_reason = "resetprop";
         return Error() << "Failed to reset sys.powerctl property";
     }
     std::vector<Service*> stop_first;
@@ -800,18 +804,22 @@
     StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
     if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
         r > 0) {
+        sub_reason = "sigkill";
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " post-data services are still running";
     }
     if (auto result = KillZramBackingDevice(); !result.ok()) {
+        sub_reason = "zram";
         return result;
     }
     if (auto result = CallVdc("volume", "reset"); !result.ok()) {
+        sub_reason = "vold_reset";
         return result;
     }
     if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
                                              sigkill_timeout, false /* SIGKILL */);
         r > 0) {
+        sub_reason = "sigkill_debug";
         // TODO(b/135984674): store information about offending services for debugging.
         return Error() << r << " debugging services are still running";
     }
@@ -822,9 +830,11 @@
         LOG(INFO) << "sync() took " << sync_timer;
     }
     if (auto result = UnmountAllApexes(); !result.ok()) {
+        sub_reason = "apex";
         return result;
     }
     if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+        sub_reason = "ns_switch";
         return Error() << "Failed to switch to bootstrap namespace";
     }
     // Remove services that were defined in an APEX.
diff --git a/init/service.cpp b/init/service.cpp
index 20400a0..165b848 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -513,6 +513,10 @@
             LOG(ERROR) << "failed to write pid to files: " << result.error();
         }
 
+        if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+            LOG(ERROR) << "failed to set task profiles";
+        }
+
         // As requested, set our gid, supplemental gids, uid, context, and
         // priority. Aborts on failure.
         SetProcessAttributesAndCaps();
diff --git a/init/service.h b/init/service.h
index 9f1d697..34ed5ef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -170,6 +170,8 @@
 
     std::vector<std::string> writepid_files_;
 
+    std::vector<std::string> task_profiles_;
+
     std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name
 
     // keycodes for triggering this service via /dev/input/input*
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 560f693..bdac077 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -360,6 +360,12 @@
     return Error() << "Invalid shutdown option";
 }
 
+Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    service_->task_profiles_ = std::move(args);
+    return {};
+}
+
 Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
     int period;
     if (!ParseInt(args[1], &period, 1)) {
@@ -529,6 +535,7 @@
         {"sigstop",                 {0,     0,    &ServiceParser::ParseSigstop}},
         {"socket",                  {3,     6,    &ServiceParser::ParseSocket}},
         {"stdio_to_kmsg",           {0,     0,    &ServiceParser::ParseStdioToKmsg}},
+        {"task_profiles",           {1,     kMax, &ServiceParser::ParseTaskProfiles}},
         {"timeout_period",          {1,     1,    &ServiceParser::ParseTimeoutPeriod}},
         {"updatable",               {0,     0,    &ServiceParser::ParseUpdatable}},
         {"user",                    {1,     1,    &ServiceParser::ParseUser}},
@@ -598,6 +605,13 @@
         }
     }
 
+    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+        if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
+            return Error() << "service '" << service_->name()
+                           << "' can't be both critical and oneshot";
+        }
+    }
+
     Service* old_service = service_list_->FindService(service_->name());
     if (old_service) {
         if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 7bb0cc0..0fd2da5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -78,6 +78,7 @@
     Result<void> ParseSigstop(std::vector<std::string>&& args);
     Result<void> ParseSocket(std::vector<std::string>&& args);
     Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
+    Result<void> ParseTaskProfiles(std::vector<std::string>&& args);
     Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
     Result<void> ParseFile(std::vector<std::string>&& args);
     Result<void> ParseUser(std::vector<std::string>&& args);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 5263c14..f3dd538 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -52,6 +52,8 @@
 namespace {
 
 std::string shutdown_command;
+static bool subcontext_terminated_by_shutdown;
+static std::unique_ptr<Subcontext> subcontext;
 
 class SubcontextProcess {
   public:
@@ -323,34 +325,30 @@
     return expanded_args;
 }
 
-static std::vector<Subcontext> subcontexts;
-static bool shutting_down;
-
-std::unique_ptr<Subcontext> InitializeSubcontext() {
+void InitializeSubcontext() {
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
-                                            kVendorContext);
+        subcontext.reset(
+                new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
     }
-    return nullptr;
+}
+
+Subcontext* GetSubcontext() {
+    return subcontext.get();
 }
 
 bool SubcontextChildReap(pid_t pid) {
-    for (auto& subcontext : subcontexts) {
-        if (subcontext.pid() == pid) {
-            if (!shutting_down) {
-                subcontext.Restart();
-            }
-            return true;
+    if (subcontext->pid() == pid) {
+        if (!subcontext_terminated_by_shutdown) {
+            subcontext->Restart();
         }
+        return true;
     }
     return false;
 }
 
 void SubcontextTerminate() {
-    shutting_down = true;
-    for (auto& subcontext : subcontexts) {
-        kill(subcontext.pid(), SIGTERM);
-    }
+    subcontext_terminated_by_shutdown = true;
+    kill(subcontext->pid(), SIGTERM);
 }
 
 }  // namespace init
diff --git a/init/subcontext.h b/init/subcontext.h
index 5e1d8a8..788d3be 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -60,7 +60,8 @@
 };
 
 int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::unique_ptr<Subcontext> InitializeSubcontext();
+void InitializeSubcontext();
+Subcontext* GetSubcontext();
 bool SubcontextChildReap(pid_t pid);
 void SubcontextTerminate();
 
diff --git a/init/util.cpp b/init/util.cpp
index 24f94ec..f9be055 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,7 +30,9 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <mutex>
 #include <thread>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -660,5 +662,50 @@
     return access("/system/bin/recovery", F_OK) == 0;
 }
 
+// TODO(b/155203339): remove this
+// Devices in the lab seem to be stuck during shutdown, but the logs don't capture the last actions
+// before shutdown started, so we record those lines, ignoring requests to shutdown, and replay them
+// if we identify that the device is stuck.
+constexpr size_t kRecordedLogsSize = 30;
+std::string recorded_logs[kRecordedLogsSize];
+size_t recorded_log_position = 0;
+std::mutex recorded_logs_lock;
+
+void InitSecondStageLogging(char** argv) {
+    SetFatalRebootTarget();
+    auto second_stage_logger = [](android::base::LogId log_id, android::base::LogSeverity severity,
+                                  const char* tag, const char* file, unsigned int line,
+                                  const char* message) {
+        // We only store logs for init, not its children, and only if they're not related to
+        // sys.powerctl.
+        if (getpid() == 1 && strstr(message, "sys.powerctl") == nullptr) {
+            auto lock = std::lock_guard{recorded_logs_lock};
+            recorded_logs[recorded_log_position++] = message;
+            if (recorded_log_position == kRecordedLogsSize) {
+                recorded_log_position = 0;
+            }
+        }
+        android::base::KernelLogger(log_id, severity, tag, file, line, message);
+    };
+    android::base::InitLogging(argv, second_stage_logger, InitAborter);
+}
+
+void DumpShutdownDebugInformation() {
+    auto lock = std::lock_guard{recorded_logs_lock};
+    android::base::KernelLogger(
+            android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+            "===================== Dumping previous init lines =====================");
+    for (size_t i = recorded_log_position; i < kRecordedLogsSize; ++i) {
+        android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+                                    recorded_logs[i].c_str());
+    }
+    for (size_t i = 0; i < recorded_log_position; ++i) {
+        android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+                                    recorded_logs[i].c_str());
+    }
+    android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+                                "===================== End of dump =====================");
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index ad322d9..8167b02 100644
--- a/init/util.h
+++ b/init/util.h
@@ -78,6 +78,8 @@
 
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
+void InitSecondStageLogging(char** argv);
+void DumpShutdownDebugInformation();
 bool IsRecoveryMode();
 }  // namespace init
 }  // namespace android
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..1913c1e 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -1,14 +1,20 @@
-/* cutils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed.  It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
+/*
+ * Copyright (C) 2008 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.
  */
 
-#ifndef _CUTILS_ASHMEM_H
-#define _CUTILS_ASHMEM_H
+#pragma once
 
 #include <stddef.h>
 
@@ -30,5 +36,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif	/* _CUTILS_ASHMEM_H */
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 0b98e1a..6051ac7 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    min_sdk_version: "29",
     native_bridge_supported: true,
     export_include_dirs: ["include"],
     system_shared_libs: [],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index f27b8e6..820b7cb 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -144,8 +144,11 @@
 /*
  * Release any logger resources (a new log write will immediately re-acquire)
  *
- * May be used to clean up File descriptors after a Fork, the resources are
- * all O_CLOEXEC so wil self clean on exec().
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization.  O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
  */
 void __android_log_close(void);
 
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c8fafe7..8e4faeb 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -23,14 +23,6 @@
 #endif
 
 /*
- * Send a simple string to the log.
- */
-int __android_log_buf_write(int bufID, int prio, const char* tag,
-                            const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
-    __attribute__((__format__(printf, 4, 5)));
-
-/*
  * log_id_t helpers
  */
 log_id_t android_name_to_log_id(const char* logName);
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3a8af6d..3497d63 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -1,11 +1,18 @@
 /*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed.  It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 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
 
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index 8b8a362..f5525c1 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <android/log.h>
-#include <log/log_id.h>
 
 /*
  * Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index ffd3b52..24b88d2 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -19,7 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <log/log_id.h>
+#include <android/log.h>
 #include <log/log_time.h>
 
 #ifdef __cplusplus
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index d3e9b19..b2604b5 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -1,11 +1,18 @@
 /*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed.  It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 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
 
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index eaec741..6f40515 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <android/log.h>
-#include <log/log_id.h>
 
 /*
  * Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 67376f4..a230749 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -32,58 +32,53 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <shared_mutex>
-
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
-#include "rwlock.h"
 #include "uio.h"
 
-static int logd_socket;
-static RwLock logd_socket_lock;
+static atomic_int logd_socket;
 
-static void OpenSocketLocked() {
-  logd_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
-  if (logd_socket <= 0) {
-    return;
-  }
-
+// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+// function is used to reconnect to logd without requiring a new socket.
+static void LogdConnect() {
   sockaddr_un un = {};
   un.sun_family = AF_UNIX;
   strcpy(un.sun_path, "/dev/socket/logdw");
-
-  if (TEMP_FAILURE_RETRY(
-          connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un))) < 0) {
-    close(logd_socket);
-    logd_socket = 0;
-  }
+  TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
 }
 
-static void OpenSocket() {
-  auto lock = std::unique_lock{logd_socket_lock};
-  if (logd_socket > 0) {
-    // Someone raced us and opened the socket already.
+// logd_socket should only be opened once.  If we see that logd_socket is uninitialized, we create a
+// new socket and attempt to exchange it into the atomic logd_socket.  If the compare/exchange was
+// successful, then that will be the socket used for the duration of the program, otherwise a
+// different thread has already opened and written the socket to the atomic, so close the new socket
+// and return.
+static void GetSocket() {
+  if (logd_socket != 0) {
     return;
   }
 
-  OpenSocketLocked();
-}
-
-static void ResetSocket(int old_socket) {
-  auto lock = std::unique_lock{logd_socket_lock};
-  if (old_socket != logd_socket) {
-    // Someone raced us and reset the socket already.
+  int new_socket =
+      TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+  if (new_socket <= 0) {
     return;
   }
-  close(logd_socket);
-  logd_socket = 0;
-  OpenSocketLocked();
+
+  int uninitialized_value = 0;
+  if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
+    close(new_socket);
+    return;
+  }
+
+  LogdConnect();
 }
 
+// This is the one exception to the above.  Zygote uses this to clean up open FD's after fork() and
+// before specialization.  It is single threaded at this point and therefore this function is
+// explicitly not thread safe.  It sets logd_socket to 0, so future logs will be safely initialized
+// whenever they happen.
 void LogdClose() {
-  auto lock = std::unique_lock{logd_socket_lock};
   if (logd_socket > 0) {
     close(logd_socket);
   }
@@ -99,12 +94,7 @@
   static atomic_int dropped;
   static atomic_int droppedSecurity;
 
-  auto lock = std::shared_lock{logd_socket_lock};
-  if (logd_socket <= 0) {
-    lock.unlock();
-    OpenSocket();
-    lock.lock();
-  }
+  GetSocket();
 
   if (logd_socket <= 0) {
     return -EBADF;
@@ -183,10 +173,7 @@
   // the connection, so we reset it and try again.
   ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
   if (ret < 0 && errno != EAGAIN) {
-    int old_socket = logd_socket;
-    lock.unlock();
-    ResetSocket(old_socket);
-    lock.lock();
+    LogdConnect();
 
     ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
   }
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..0751e2c 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -23,30 +23,36 @@
 #include <sys/types.h>
 #include <time.h>
 
-#include <shared_mutex>
-
 #include <log/log_properties.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
-#include "rwlock.h"
 #include "uio.h"
 
-static int pmsg_fd;
-static RwLock pmsg_fd_lock;
+static atomic_int pmsg_fd;
 
-static void PmsgOpen() {
-  auto lock = std::unique_lock{pmsg_fd_lock};
-  if (pmsg_fd > 0) {
-    // Someone raced us and opened the socket already.
+// pmsg_fd should only beopened once.  If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd.  If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+  if (pmsg_fd != 0) {
     return;
   }
 
-  pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+  int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+  if (new_fd <= 0) {
+    return;
+  }
+
+  int uninitialized_value = 0;
+  if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+    close(new_fd);
+    return;
+  }
 }
 
 void PmsgClose() {
-  auto lock = std::unique_lock{pmsg_fd_lock};
   if (pmsg_fd > 0) {
     close(pmsg_fd);
   }
@@ -77,13 +83,7 @@
     }
   }
 
-  auto lock = std::shared_lock{pmsg_fd_lock};
-
-  if (pmsg_fd <= 0) {
-    lock.unlock();
-    PmsgOpen();
-    lock.lock();
-  }
+  GetPmsgFd();
 
   if (pmsg_fd <= 0) {
     return -EBADF;
diff --git a/liblog/rwlock.h b/liblog/rwlock.h
deleted file mode 100644
index 00f1806..0000000
--- a/liblog/rwlock.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 <pthread.h>
-
-// As of the end of Dec 2019, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
-// combination of std::mutex and std::condition variable, which is obviously less efficient.  This
-// immitates what std::shared_mutex should be doing and is compatible with std::shared_lock and
-// std::unique_lock.
-
-class RwLock {
- public:
-  RwLock() {}
-  ~RwLock() {}
-
-  void lock() { pthread_rwlock_wrlock(&rwlock_); }
-  void unlock() { pthread_rwlock_unlock(&rwlock_); }
-
-  void lock_shared() { pthread_rwlock_rdlock(&rwlock_); }
-  void unlock_shared() { pthread_rwlock_unlock(&rwlock_); }
-
- private:
-  pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
-};
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 385b079..2a6424b 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -63,8 +63,8 @@
         "log_system_test.cpp",
         "log_time_test.cpp",
         "log_wrap_test.cpp",
+        "logd_writer_test.cpp",
         "logprint_test.cpp",
-        "rwlock_test.cpp",
     ],
     shared_libs: [
         "libcutils",
@@ -72,6 +72,7 @@
     ],
     static_libs: ["liblog"],
     isolated: true,
+    require_root: true,
 }
 
 // Build tests for the device (with .so). Run with:
@@ -108,7 +109,6 @@
         "liblog_host_test.cpp",
         "liblog_default_tag.cpp",
         "liblog_global_state.cpp",
-        "rwlock_test.cpp",
     ],
     isolated: true,
 }
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 0000000..b8e4726
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
+// socket.  This tests for that behavior.
+TEST(liblog, multi_connect_dgram_socket) {
+#ifdef __ANDROID__
+  if (getuid() != 0) {
+    GTEST_SKIP() << "Skipping test, must be run as root.";
+    return;
+  }
+  auto temp_dir = TemporaryDir();
+  auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
+
+  unique_fd server_socket;
+
+  auto open_server_socket = [&] {
+    server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+    ASSERT_TRUE(server_socket.ok());
+
+    sockaddr_un server_sockaddr = {};
+    server_sockaddr.sun_family = AF_UNIX;
+    strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
+    ASSERT_EQ(0,
+              TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
+                                      sizeof(server_sockaddr))));
+  };
+
+  // Open the server socket.
+  open_server_socket();
+
+  // Open the client socket.
+  auto client_socket =
+      unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
+  ASSERT_TRUE(client_socket.ok());
+  sockaddr_un client_sockaddr = {};
+  client_sockaddr.sun_family = AF_UNIX;
+  strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
+  ASSERT_EQ(0,
+            TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+                                       sizeof(client_sockaddr))));
+
+  // Ensure that communication works.
+  constexpr static char kSmoke[] = "smoke test";
+  ssize_t smoke_len = sizeof(kSmoke);
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+  char read_buf[512];
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+  ASSERT_STREQ(kSmoke, read_buf);
+
+  // Close the server socket.
+  server_socket.reset();
+  ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
+
+  // Ensure that write() from the client returns an error since the server is closed.
+  ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+  ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
+
+  // Open the server socket again.
+  open_server_socket();
+
+  // Reconnect the same client socket.
+  ASSERT_EQ(0,
+            TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+                                       sizeof(client_sockaddr))))
+      << strerror(errno);
+
+  // Ensure that communication works.
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+  ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+  ASSERT_STREQ(kSmoke, read_buf);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/rwlock_test.cpp b/liblog/tests/rwlock_test.cpp
deleted file mode 100644
index 617d5c4..0000000
--- a/liblog/tests/rwlock_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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 "../rwlock.h"
-
-#include <chrono>
-#include <shared_mutex>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace std::literals;
-
-TEST(rwlock, reader_then_reader_lock) {
-  RwLock lock;
-
-  bool thread_ran = false;
-  auto read_guard = std::shared_lock{lock};
-
-  auto reader_thread = std::thread([&] {
-    auto read_guard = std::shared_lock{lock};
-    thread_ran = true;
-  });
-
-  auto end_time = std::chrono::steady_clock::now() + 1s;
-
-  while (std::chrono::steady_clock::now() < end_time) {
-    if (thread_ran) {
-      break;
-    }
-  }
-
-  EXPECT_EQ(true, thread_ran);
-
-  // Unlock the lock in case something went wrong, to ensure that we can still join() the thread.
-  read_guard.unlock();
-  reader_thread.join();
-}
-
-template <template <typename> typename L1, template <typename> typename L2>
-void TestBlockingLocks() {
-  RwLock lock;
-
-  bool thread_ran = false;
-  auto read_guard = L1{lock};
-
-  auto reader_thread = std::thread([&] {
-    auto read_guard = L2{lock};
-    thread_ran = true;
-  });
-
-  auto end_time = std::chrono::steady_clock::now() + 1s;
-
-  while (std::chrono::steady_clock::now() < end_time) {
-    if (thread_ran) {
-      break;
-    }
-  }
-
-  EXPECT_EQ(false, thread_ran);
-
-  read_guard.unlock();
-  reader_thread.join();
-
-  EXPECT_EQ(true, thread_ran);
-}
-
-TEST(rwlock, reader_then_writer_lock) {
-  TestBlockingLocks<std::shared_lock, std::unique_lock>();
-}
-
-TEST(rwlock, writer_then_reader_lock) {
-  TestBlockingLocks<std::unique_lock, std::shared_lock>();
-}
-
-TEST(rwlock, writer_then_writer_lock) {
-  TestBlockingLocks<std::unique_lock, std::unique_lock>();
-}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 12474f1..766ea0f 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -100,13 +100,12 @@
         "libjsoncpp",
         "libprotobuf-cpp-full",
     ],
-    target: {
-        android: {
-            test_config: "vts_processgroup_validate_test.xml",
-        },
-    },
+    test_suites: [
+        "vts",
+    ],
 }
 
 vts_config {
     name: "VtsProcessgroupValidateTest",
+    test_config: "vts_processgroup_validate_test.xml",
 }
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 3f08535..a515e58 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -144,6 +144,19 @@
         }
       ]
     },
+    {
+      "Name": "CameraServicePerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "camera-daemon"
+          }
+        }
+      ]
+    },
 
     {
       "Name": "CpuPolicySpread",
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index caea048..cbc65ff 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -48,6 +48,7 @@
     export_include_dirs: ["include"],
     static_libs: ["libgtest_prod"],
     apex_available: ["com.android.resolv"],
+    min_sdk_version: "29",
 }
 
 cc_test {
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 690dc94..6882ab2 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -55,6 +55,7 @@
     export_include_dirs: ["include"],
     host_supported: true,
     apex_available: ["com.android.resolv"],
+    min_sdk_version: "29",
 }
 
 cc_benchmark {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index bf7d69e..f3d3f27 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -75,13 +75,29 @@
     ],
 
     target: {
-        // Always disable optimizations for host to make it easier to debug.
         host: {
+            // Always disable optimizations for host to make it easier to debug.
             cflags: [
                 "-O0",
                 "-g",
             ],
         },
+        android: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
+        },
+        linux_bionic: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
+        },
     },
 
     arch: {
@@ -102,16 +118,6 @@
         "liblog",
         "liblzma",
     ],
-
-    header_libs: [
-        "bionic_libc_platform_headers",
-    ],
-
-    product_variables: {
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
-        },
-    },
 }
 
 cc_library {
@@ -281,13 +287,22 @@
         "tests/files/offline/straddle_arm64/*",
     ],
 
-    header_libs: [
-        "bionic_libc_platform_headers",
-    ],
-
-    product_variables: {
-        experimental_mte: {
-            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+    target: {
+        android: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
+        },
+        linux_bionic: {
+            header_libs: ["bionic_libc_platform_headers"],
+            product_variables: {
+                experimental_mte: {
+                    cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+                },
+            },
         },
     },
 }
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
index d1d0ebc..46a546e 100644
--- a/libunwindstack/MemoryMte.cpp
+++ b/libunwindstack/MemoryMte.cpp
@@ -17,6 +17,7 @@
 #if defined(ANDROID_EXPERIMENTAL_MTE)
 
 #include <sys/ptrace.h>
+#include <sys/uio.h>
 
 #include <bionic/mte.h>
 #include <bionic/mte_kernel.h>
@@ -28,7 +29,13 @@
 
 long MemoryRemote::ReadTag(uint64_t addr) {
 #if defined(__aarch64__)
-  return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
+  char tag;
+  iovec iov = {&tag, 1};
+  if (ptrace(PTRACE_PEEKMTETAGS, pid_, reinterpret_cast<void*>(addr), &iov) != 0 ||
+      iov.iov_len != 1) {
+    return -1;
+  }
+  return tag;
 #else
   (void)addr;
   return -1;
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 385078d..621893b 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -28,8 +28,6 @@
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
-#include <bionic/mte.h>
-#include <bionic/mte_kernel.h>
 #include <gtest/gtest.h>
 
 #include "MemoryRemote.h"
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1f8ad05..36273de 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -46,9 +46,6 @@
 
 void LogBuffer::init() {
     log_id_for_each(i) {
-        mLastSet[i] = false;
-        mLast[i] = mLogElements.begin();
-
         if (setSize(i, __android_logger_get_buffer_size(i))) {
             setSize(i, LOG_BUFFER_MIN_SIZE);
         }
@@ -131,6 +128,20 @@
     }
 }
 
+LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
+    auto it = mLogElements.begin();
+    if (mOldest[log_id]) {
+        it = *mOldest[log_id];
+    }
+    while (it != mLogElements.end() && (*it)->getLogId() != log_id) {
+        it++;
+    }
+    if (it != mLogElements.end()) {
+        mOldest[log_id] = it;
+    }
+    return it;
+}
+
 enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
 
 static enum match_type identical(LogBufferElement* elem,
@@ -450,9 +461,7 @@
 
     bool setLast[LOG_ID_MAX];
     bool doSetLast = false;
-    log_id_for_each(i) {
-        doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
-    }
+    log_id_for_each(i) { doSetLast |= setLast[i] = mOldest[i] && it == *mOldest[i]; }
 #ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
     LogBufferElementCollection::iterator bad = it;
     int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
@@ -463,11 +472,11 @@
     if (doSetLast) {
         log_id_for_each(i) {
             if (setLast[i]) {
-                if (__predict_false(it == mLogElements.end())) {  // impossible
-                    mLastSet[i] = false;
-                    mLast[i] = mLogElements.begin();
+                if (__predict_false(it == mLogElements.end())) {
+                    mOldest[i] = std::nullopt;
                 } else {
-                    mLast[i] = it;  // push down the road as next-best-watermark
+                    mOldest[i] = it;  // Store the next iterator even if it does not correspond to
+                                      // the same log_id, as a starting point for GetOldest().
                 }
             }
         }
@@ -486,11 +495,6 @@
                                  b.first);
             }
         }
-        if (mLastSet[i] && (bad == mLast[i])) {
-            android::prdebug("stale mLast[%d]\n", i);
-            mLastSet[i] = false;
-            mLast[i] = mLogElements.begin();
-        }
     }
 #endif
     if (coalesce) {
@@ -668,7 +672,7 @@
     if (__predict_false(caller_uid != AID_ROOT)) {  // unlikely
         // Only here if clear all request from non system source, so chatty
         // filter logistics is not required.
-        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        it = GetOldest(id);
         while (it != mLogElements.end()) {
             LogBufferElement* element = *it;
 
@@ -678,11 +682,6 @@
                 continue;
             }
 
-            if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
-                mLast[id] = it;
-                mLastSet[id] = true;
-            }
-
             if (oldest && oldest->mStart <= element->getSequence()) {
                 busy = true;
                 kickMe(oldest, id, pruneRows);
@@ -734,8 +733,8 @@
         }
 
         bool kick = false;
-        bool leading = true;
-        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        bool leading = true;  // true if starting from the oldest log entry, false if starting from
+                              // a specific chatty entry.
         // Perform at least one mandatory garbage collection cycle in following
         // - clear leading chatty tags
         // - coalesce chatty tags
@@ -763,6 +762,9 @@
                 }
             }
         }
+        if (leading) {
+            it = GetOldest(id);
+        }
         static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
         LogBufferElementCollection::iterator lastt;
         lastt = mLogElements.end();
@@ -783,11 +785,6 @@
             }
             // below this point element->getLogId() == id
 
-            if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
-                mLast[id] = it;
-                mLastSet[id] = true;
-            }
-
             uint16_t dropped = element->getDropped();
 
             // remove any leading drops
@@ -909,7 +906,7 @@
 
     bool whitelist = false;
     bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
-    it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+    it = GetOldest(id);
     while ((pruneRows > 0) && (it != mLogElements.end())) {
         LogBufferElement* element = *it;
 
@@ -918,11 +915,6 @@
             continue;
         }
 
-        if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
-            mLast[id] = it;
-            mLastSet[id] = true;
-        }
-
         if (oldest && oldest->mStart <= element->getSequence()) {
             busy = true;
             if (!whitelist) kickMe(oldest, id, pruneRows);
@@ -942,7 +934,7 @@
 
     // Do not save the whitelist if we are reader range limited
     if (whitelist && (pruneRows > 0)) {
-        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        it = GetOldest(id);
         while ((it != mLogElements.end()) && (pruneRows > 0)) {
             LogBufferElement* element = *it;
 
@@ -951,11 +943,6 @@
                 continue;
             }
 
-            if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
-                mLast[id] = it;
-                mLastSet[id] = true;
-            }
-
             if (oldest && oldest->mStart <= element->getSequence()) {
                 busy = true;
                 kickMe(oldest, id, pruneRows);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 16225a5..458fbbb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include <list>
+#include <optional>
 #include <string>
 
 #include <android/log.h>
@@ -81,9 +82,9 @@
     LogStatistics stats;
 
     PruneList mPrune;
-    // watermark for last per log id
-    LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
-    bool mLastSet[LOG_ID_MAX];
+    // Keeps track of the iterator to the oldest log message of a given log type, as an
+    // optimization when pruning logs.  Use GetOldest() to retrieve.
+    std::optional<LogBufferElementCollection::iterator> mOldest[LOG_ID_MAX];
     // watermark of any worst/chatty uid processing
     typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
         LogBufferIteratorMap;
@@ -181,6 +182,10 @@
     bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
     LogBufferElementCollection::iterator erase(
         LogBufferElementCollection::iterator it, bool coalesce = false);
+
+    // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
+    // there are no logs for the given log type. Requires mLogElementsLock to be held.
+    LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
 };
 
 #endif  // _LOGD_LOG_BUFFER_H__
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index b092489..1dd5c86 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -583,6 +583,7 @@
         "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
 }
 
+#ifdef ENABLE_FLAKY_TESTS
 // b/26447386 refined behavior
 TEST(logd, timeout) {
 #ifdef __ANDROID__
@@ -717,6 +718,7 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif
 
 // b/27242723 confirmed fixed
 TEST(logd, SNDTIMEO) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6564e8f..a380ebb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -78,6 +78,9 @@
     mkdir /dev/boringssl 0755 root root
     mkdir /dev/boringssl/selftest 0755 root root
 
+    # Mount tracefs
+    mount tracefs tracefs /sys/kernel/tracing
+
 # Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
 on early-init && property:ro.product.cpu.abilist32=*
     exec_start boringssl_self_test32