Merge "Snap for 6686656 from b371af9e0fcd1d28dfb72541bbc4a57c762601f0 to sdk-release" into sdk-release
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index d6f536e..ea50f59 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -876,7 +876,7 @@
                     "-S",
                     std::to_string(sb.st_size),
                     session_id_str,
-                    android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+                    android::base::StringPrintf("%d_%s", i, android::base::Basename(split).c_str()),
                     "-",
             };
 
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index b674a81..db4c479 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -400,12 +400,12 @@
     for (const std::string& path : paths) {
         int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
         if (wd < 0) {
-            PLOG(ERROR) << "failed to inotify_add_watch on path '" << path;
+            PLOG(ERROR) << "failed to inotify_add_watch on path '" << path << "'";
             continue;
         }
 
         g_monitored_paths[wd] = path;
-        LOG(INFO) << "watch descriptor " << wd << " registered for " << path;
+        LOG(INFO) << "watch descriptor " << wd << " registered for '" << path << "'";
     }
 
     fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
diff --git a/code_coverage/seccomp_policy/code_coverage.arm.policy b/code_coverage/seccomp_policy/code_coverage.arm.policy
index d6784e3..b80910f 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm.policy
@@ -6,6 +6,7 @@
 write: 1
 fcntl64: 1
 fstat64: 1
+ftruncate64: 1
 geteuid32: 1
 _llseek: 1
 mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.arm64.policy b/code_coverage/seccomp_policy/code_coverage.arm64.policy
index 4c3dd26..7040ea2 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm64.policy
@@ -6,6 +6,7 @@
 write: 1
 fcntl: 1
 fstat: 1
+ftruncate: 1
 geteuid: 1
 lseek: 1
 mmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.policy.def b/code_coverage/seccomp_policy/code_coverage.policy.def
index f136084..599c4a4 100644
--- a/code_coverage/seccomp_policy/code_coverage.policy.def
+++ b/code_coverage/seccomp_policy/code_coverage.policy.def
@@ -22,6 +22,7 @@
 #if     defined(__LP64__)
 fcntl: 1
 fstat: 1
+ftruncate: 1
 geteuid: 1
 lseek: 1
 mmap: 1
@@ -29,6 +30,7 @@
 #else
 fcntl64: 1
 fstat64: 1
+ftruncate64: 1
 geteuid32: 1
 _llseek: 1
 mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86.policy b/code_coverage/seccomp_policy/code_coverage.x86.policy
index 24ff8b9..f8e0cc0 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86.policy
@@ -6,6 +6,7 @@
 write: 1
 fcntl64: 1
 fstat64: 1
+ftruncate64: 1
 geteuid32: 1
 _llseek: 1
 mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86_64.policy b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
index 3081036..dcf2f9a 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86_64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
@@ -6,6 +6,7 @@
 write: 1
 fcntl: 1
 fstat: 1
+ftruncate: 1
 geteuid: 1
 lseek: 1
 mmap: 1
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index cb1d354..cc1366c 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo modem odm oem product radio recovery system vbmeta vendor"
+        partitions="boot bootloader dtbo modem odm oem product radio recovery system vbmeta vendor vendor_dlkm"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 86cf30d..4ca6a6a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -166,6 +166,10 @@
                   "vendor_boot.img",  "vendor_boot.sig",
                                                       "vendor_boot",
                                                                   true,  ImageType::BootCritical },
+    { "vendor_dlkm",
+                  "vendor_dlkm.img",  "vendor_dlkm.sig",
+                                                      "vendor_dlkm",
+                                                                  true,  ImageType::Normal },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 250cb82..8788b5a 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -280,5 +280,12 @@
     return android::base::Join(argv, " ");
 }
 
+std::string DmTargetUser::GetParameterString() const {
+    std::vector<std::string> argv;
+    argv.push_back(std::to_string(start()));
+    argv.push_back(std::to_string(size()));
+    return android::base::Join(argv, " ");
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index f986cfe..57e3884 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -309,6 +309,14 @@
     bool is_hw_wrapped_ = false;
 };
 
+class DmTargetUser final : public DmTarget {
+  public:
+    DmTargetUser(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+    std::string name() const override { return "user"; }
+    std::string GetParameterString() const override;
+};
+
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index ace6210..623293e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -31,6 +31,22 @@
 namespace android {
 namespace fs_mgr {
 
+std::ostream& operator<<(std::ostream& os, const Extent& extent) {
+    switch (extent.GetExtentType()) {
+        case ExtentType::kZero: {
+            os << "type: Zero";
+            break;
+        }
+        case ExtentType::kLinear: {
+            auto linear_extent = static_cast<const LinearExtent*>(&extent);
+            os << "type: Linear, physical sectors: " << linear_extent->physical_sector()
+               << ", end sectors: " << linear_extent->end_sector();
+            break;
+        }
+    }
+    return os;
+}
+
 bool LinearExtent::AddTo(LpMetadata* out) const {
     if (device_index_ >= out->block_devices.size()) {
         LERROR << "Extent references unknown block device.";
@@ -41,6 +57,17 @@
     return true;
 }
 
+bool LinearExtent::operator==(const android::fs_mgr::Extent& other) const {
+    if (other.GetExtentType() != ExtentType::kLinear) {
+        return false;
+    }
+
+    auto other_ptr = static_cast<const LinearExtent*>(&other);
+    return num_sectors_ == other_ptr->num_sectors_ &&
+           physical_sector_ == other_ptr->physical_sector_ &&
+           device_index_ == other_ptr->device_index_;
+}
+
 bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
     if (device_index_ != other.device_index()) {
         return false;
@@ -64,6 +91,10 @@
     return true;
 }
 
+bool ZeroExtent::operator==(const android::fs_mgr::Extent& other) const {
+    return other.GetExtentType() == ExtentType::kZero && num_sectors_ == other.num_sectors();
+}
+
 Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
     : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
 
@@ -518,7 +549,7 @@
     return partitions_.back().get();
 }
 
-Partition* MetadataBuilder::FindPartition(std::string_view name) {
+Partition* MetadataBuilder::FindPartition(std::string_view name) const {
     for (const auto& partition : partitions_) {
         if (partition->name() == name) {
             return partition.get();
@@ -527,7 +558,7 @@
     return nullptr;
 }
 
-PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) const {
     for (const auto& group : groups_) {
         if (group->name() == group_name) {
             return group.get();
@@ -1270,5 +1301,50 @@
     return geometry_.logical_block_size;
 }
 
+bool MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+        const MetadataBuilder& source_metadata, uint32_t source_slot_number,
+        const MetadataBuilder& target_metadata, uint32_t target_slot_number,
+        const std::vector<std::string>& partitions) {
+    for (const auto& base_name : partitions) {
+        // Find the partition in metadata with the slot suffix.
+        auto target_partition_name = base_name + SlotSuffixForSlotNumber(target_slot_number);
+        const auto target_partition = target_metadata.FindPartition(target_partition_name);
+        if (!target_partition) {
+            LERROR << "Failed to find partition " << target_partition_name << " in metadata slot "
+                   << target_slot_number;
+            return false;
+        }
+
+        auto source_partition_name = base_name + SlotSuffixForSlotNumber(source_slot_number);
+        const auto source_partition = source_metadata.FindPartition(source_partition_name);
+        if (!source_partition) {
+            LERROR << "Failed to find partition " << source_partition << " in metadata slot "
+                   << source_slot_number;
+            return false;
+        }
+
+        // We expect the partitions in the target metadata to have the identical extents as the
+        // one in the source metadata. Because they are copied in NewForUpdate.
+        if (target_partition->extents().size() != source_partition->extents().size()) {
+            LERROR << "Extents count mismatch for partition " << base_name << " target slot has "
+                   << target_partition->extents().size() << ", source slot has "
+                   << source_partition->extents().size();
+            return false;
+        }
+
+        for (size_t i = 0; i < target_partition->extents().size(); i++) {
+            const auto& src_extent = *source_partition->extents()[i];
+            const auto& tgt_extent = *target_partition->extents()[i];
+            if (tgt_extent != src_extent) {
+                LERROR << "Extents " << i << " is different for partition " << base_name;
+                LERROR << "tgt extent " << tgt_extent << "; src extent " << src_extent;
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 1a3250a..a21e09e 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -947,9 +947,10 @@
 }
 
 static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
-                         const std::string& partition_name, uint64_t num_sectors,
-                         uint64_t start_sector, std::vector<Interval>* intervals) {
-    Partition* p = builder->AddPartition(partition_name, "group", 0);
+                         const std::string& partition_name, const std::string& group_name,
+                         uint64_t num_sectors, uint64_t start_sector,
+                         std::vector<Interval>* intervals = nullptr) {
+    Partition* p = builder->AddPartition(partition_name, group_name, 0);
     ASSERT_NE(p, nullptr);
     ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
     ASSERT_EQ(p->extents().size(), 1);
@@ -977,17 +978,17 @@
     ASSERT_TRUE(builder->AddGroup("group", 0));
 
     std::vector<Interval> old_intervals;
-    AddPartition(builder, "system", 10229008, 2048, &old_intervals);
-    AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
-    AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
-    AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
-    AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
-    AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
-    AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
-    AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+    AddPartition(builder, "system", "group", 10229008, 2048, &old_intervals);
+    AddPartition(builder, "test_a", "group", 648, 12709888, &old_intervals);
+    AddPartition(builder, "test_b", "group", 625184, 12711936, &old_intervals);
+    AddPartition(builder, "test_c", "group", 130912, 13338624, &old_intervals);
+    AddPartition(builder, "test_d", "group", 888, 13469696, &old_intervals);
+    AddPartition(builder, "test_e", "group", 888, 13471744, &old_intervals);
+    AddPartition(builder, "test_f", "group", 888, 13475840, &old_intervals);
+    AddPartition(builder, "test_g", "group", 888, 13477888, &old_intervals);
 
     // Don't track the first vendor interval, since it will get extended.
-    AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+    AddPartition(builder, "vendor", "group", 2477920, 10231808, nullptr);
 
     std::vector<Interval> new_intervals;
 
@@ -1066,3 +1067,30 @@
     ASSERT_NE(p, nullptr);
     ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
 }
+
+TEST_F(BuilderTest, VerifyExtent) {
+    auto source_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
+    ASSERT_NE(source_builder, nullptr);
+    ASSERT_TRUE(source_builder->AddGroup("test_group_a", 40960));
+    ASSERT_TRUE(source_builder->AddGroup("test_group_b", 40960));
+    AddPartition(source_builder, "system_a", "test_group_a", 8192, 2048);
+    AddPartition(source_builder, "vendor_a", "test_group_a", 10240, 10240);
+    AddPartition(source_builder, "system_b", "test_group_b", 8192, 20480);
+
+    auto target_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
+    ASSERT_NE(target_builder, nullptr);
+    ASSERT_TRUE(target_builder->AddGroup("test_group_b", 40960));
+    AddPartition(target_builder, "system_b", "test_group_b", 8192, 2048);
+    AddPartition(target_builder, "vendor_b", "test_group_b", 10240, 10240);
+
+    ASSERT_TRUE(MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+            *source_builder, 0, *target_builder, 1, std::vector<std::string>{"system", "vendor"}));
+
+    target_builder->RemovePartition("vendor_b");
+    ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
+            *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
+
+    AddPartition(target_builder, "vendor_b", "test_group_b", 1000, 10240);
+    ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
+            *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f2e7370..54f31bc 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -42,6 +42,11 @@
 // Name of the default group in a metadata.
 static constexpr std::string_view kDefaultGroup = "default";
 
+enum class ExtentType {
+    kZero,
+    kLinear,
+};
+
 // Abstraction around dm-targets that can be encoded into logical partition tables.
 class Extent {
   public:
@@ -50,6 +55,10 @@
 
     virtual bool AddTo(LpMetadata* out) const = 0;
     virtual LinearExtent* AsLinearExtent() { return nullptr; }
+    virtual ExtentType GetExtentType() const = 0;
+
+    virtual bool operator==(const Extent& other) const = 0;
+    virtual bool operator!=(const Extent& other) const { return !(*this == other); }
 
     uint64_t num_sectors() const { return num_sectors_; }
     void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
@@ -58,6 +67,8 @@
     uint64_t num_sectors_;
 };
 
+std::ostream& operator<<(std::ostream& os, const Extent& extent);
+
 // This corresponds to a dm-linear target.
 class LinearExtent final : public Extent {
   public:
@@ -66,6 +77,9 @@
 
     bool AddTo(LpMetadata* metadata) const override;
     LinearExtent* AsLinearExtent() override { return this; }
+    ExtentType GetExtentType() const override { return ExtentType::kLinear; }
+
+    bool operator==(const Extent& other) const override;
 
     uint64_t physical_sector() const { return physical_sector_; }
     uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
@@ -87,6 +101,9 @@
     explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
 
     bool AddTo(LpMetadata* out) const override;
+    ExtentType GetExtentType() const override { return ExtentType::kZero; }
+
+    bool operator==(const Extent& other) const override;
 };
 
 class PartitionGroup final {
@@ -243,6 +260,14 @@
         return New(device_info, metadata_max_size, metadata_slot_count);
     }
 
+    // Verifies that the given partitions in the metadata have the same extents as the source
+    // metadata.
+    static bool VerifyExtentsAgainstSourceMetadata(const MetadataBuilder& source_metadata,
+                                                   uint32_t source_slot_number,
+                                                   const MetadataBuilder& target_metadata,
+                                                   uint32_t target_slot_number,
+                                                   const std::vector<std::string>& partitions);
+
     // Define a new partition group. By default there is one group called
     // "default", with an unrestricted size. A non-zero size will restrict the
     // total space used by all partitions in the group.
@@ -267,10 +292,10 @@
     void RemovePartition(std::string_view name);
 
     // Find a partition by name. If no partition is found, nullptr is returned.
-    Partition* FindPartition(std::string_view name);
+    Partition* FindPartition(std::string_view name) const;
 
     // Find a group by name. If no group is found, nullptr is returned.
-    PartitionGroup* FindGroup(std::string_view name);
+    PartitionGroup* FindGroup(std::string_view name) const;
 
     // Add a predetermined extent to a partition.
     bool AddLinearExtent(Partition* partition, const std::string& block_device,
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 55214f5..7488bda 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1846,7 +1846,7 @@
         PLOG(ERROR) << "Open failed: " << file;
         return nullptr;
     }
-    if (lock_flags != 0 && flock(fd, lock_flags) < 0) {
+    if (lock_flags != 0 && TEMP_FAILURE_RETRY(flock(fd, lock_flags)) < 0) {
         PLOG(ERROR) << "Acquire flock failed: " << file;
         return nullptr;
     }
@@ -1857,7 +1857,7 @@
 }
 
 SnapshotManager::LockedFile::~LockedFile() {
-    if (flock(fd_, LOCK_UN) < 0) {
+    if (TEMP_FAILURE_RETRY(flock(fd_, LOCK_UN)) < 0) {
         PLOG(ERROR) << "Failed to unlock file: " << path_;
     }
 }
@@ -2520,7 +2520,19 @@
         LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
         return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
     }
-    return AutoUnmountDevice::New(device_->GetMetadataDir());
+    auto ret = AutoUnmountDevice::New(device_->GetMetadataDir());
+    if (ret == nullptr) return nullptr;
+
+    // In rescue mode, it is possible to erase and format metadata, but /metadata/ota is not
+    // created to execute snapshot updates. Hence, subsequent calls is likely to fail because
+    // Lock*() fails. By failing early and returning nullptr here, update_engine_sideload can
+    // treat this case as if /metadata is not mounted.
+    if (!LockShared()) {
+        LOG(WARNING) << "/metadata is mounted, but errors occur when acquiring a shared lock. "
+                        "Subsequent calls to SnapshotManager will fail. Unmounting /metadata now.";
+        return nullptr;
+    }
+    return ret;
 }
 
 bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 2738457..7a3d9a9 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -174,6 +174,8 @@
             }
             return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
                                                       cow_device, mode, chunk_size);
+        } else if (target_type == "user") {
+            return std::make_unique<DmTargetUser>(start_sector, num_sectors);
         } else {
             std::cerr << "Unrecognized target type: " << target_type << std::endl;
             return nullptr;
diff --git a/init/README.md b/init/README.md
index 188f19b..c3b64f6 100644
--- a/init/README.md
+++ b/init/README.md
@@ -197,11 +197,14 @@
   Currently defaults to root.  (??? probably should default to nobody)
 
 `interface <interface name> <instance name>`
-> Associates this service with a list of the HIDL services that it provides. The interface name
-  must be a fully-qualified name and not a value name. For instance, this is used to allow
-  hwservicemanager to lazily start services. When multiple interfaces are served, this tag should
-  be used multiple times.
-  For example: interface vendor.foo.bar@1.0::IBaz default
+> Associates this service with a list of the AIDL or HIDL services that it provides. The interface
+  name must be a fully-qualified name and not a value name. For instance, this is used to allow
+  servicemanager or hwservicemanager to lazily start services. When multiple interfaces are served,
+  this tag should be used multiple times. An example of an entry for a HIDL
+  interface is `interface vendor.foo.bar@1.0::IBaz default`. For an AIDL interface, use
+  `interface aidl <instance name>`. The instance name for an AIDL interface is
+  whatever is registered with servicemanager, and these can be listed with `adb
+  shell dumpsys -l`.
 
 `ioprio <class> <priority>`
 > Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall.
diff --git a/init/init.cpp b/init/init.cpp
index 631db8e..ba880ea 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -868,6 +868,8 @@
     // Run all property triggers based on current state of the properties.
     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
 
+    // Restore prio before main loop
+    setpriority(PRIO_PROCESS, 0, 0);
     while (true) {
         // By default, sleep until something happens.
         auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 07b4724..fa65740 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,6 +17,7 @@
 #include <functional>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <gtest/gtest.h>
 
 #include "action.h"
@@ -32,6 +33,8 @@
 #include "service_parser.h"
 #include "util.h"
 
+using android::base::GetIntProperty;
+
 namespace android {
 namespace init {
 
@@ -240,6 +243,10 @@
 }
 
 TEST(init, RejectsCriticalAndOneshotService) {
+    if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
+        GTEST_SKIP() << "Test only valid for devices launching with R or later";
+    }
+
     std::string init_script =
             R"init(
 service A something
diff --git a/init/main.cpp b/init/main.cpp
index 38bc74b..23f5530 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -52,7 +52,8 @@
 #if __has_feature(address_sanitizer)
     __asan_set_error_report_callback(AsanReportCallback);
 #endif
-
+    // Boost prio which will be restored later
+    setpriority(PRIO_PROCESS, 0, -20);
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
     }
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 612854d..0c4a3c4 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -51,6 +51,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -74,6 +75,7 @@
 using namespace std::literals;
 
 using android::base::GetProperty;
+using android::base::ParseInt;
 using android::base::ReadFileToString;
 using android::base::Split;
 using android::base::StartsWith;
@@ -630,9 +632,10 @@
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
-    static constexpr const char* const kVendorPathPrefixes[2] = {
+    static constexpr const char* const kVendorPathPrefixes[3] = {
             "/vendor",
             "/odm",
+            "/vendor_dlkm",
     };
 
     const char* context = kInitContext;
@@ -886,24 +889,61 @@
         load_properties_from_file("/prop.default", nullptr, &properties);
     }
 
+    // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
+    // Falling back to /<part>/defalt.prop and /<part>/build.prop only when legacy path has to
+    // be supported, which is controlled by the support_legacy_path_until argument.
+    const auto load_properties_from_partition = [&properties](const std::string& partition,
+                                                              int support_legacy_path_until) {
+        auto path = "/" + partition + "/etc/build.prop";
+        if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
+            return;
+        }
+        // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
+        // separate map. Then by comparing its value with legacy_version, we know that if the
+        // partition is old enough so that we need to respect the legacy paths.
+        std::map<std::string, std::string> temp;
+        auto legacy_path1 = "/" + partition + "/default.prop";
+        auto legacy_path2 = "/" + partition + "/build.prop";
+        load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);
+        load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);
+        bool support_legacy_path = false;
+        auto version_prop_name = "ro." + partition + ".build.version.sdk";
+        auto it = temp.find(version_prop_name);
+        if (it == temp.end()) {
+            // This is embarassing. Without the prop, we can't determine how old the partition is.
+            // Let's be conservative by assuming it is very very old.
+            support_legacy_path = true;
+        } else if (int value;
+                   ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {
+            support_legacy_path = true;
+        }
+        if (support_legacy_path) {
+            // We don't update temp into properties directly as it might skip any (future) logic
+            // for resolving duplicates implemented in load_properties_from_file.  Instead, read
+            // the files again into the properties map.
+            load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);
+            load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);
+        } else {
+            LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "
+                       << "because " << version_prop_name << "(" << it->second << ") is newer "
+                       << "than " << support_legacy_path_until;
+        }
+    };
+
+    // Order matters here. The more the partition is specific to a product, the higher its
+    // precedence is.
     load_properties_from_file("/system/build.prop", nullptr, &properties);
-    load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
-
-    // TODO(b/117892318): uncomment the following condition when vendor.imgs for
-    // aosp_* targets are all updated.
-//    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
-        load_properties_from_file("/vendor/default.prop", nullptr, &properties);
-//    }
+    load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
+    // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
+    // all updated.
+    // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
+    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+    // }
     load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+    load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
+    load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
+    load_properties_from_partition("product", /* support_legacy_path_until */ 30);
 
-    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
-        load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
-    } else {
-        load_properties_from_file("/odm/default.prop", nullptr, &properties);
-        load_properties_from_file("/odm/build.prop", nullptr, &properties);
-    }
-
-    load_properties_from_file("/product/build.prop", nullptr, &properties);
     load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
 
     if (access(kDebugRamdiskProp, R_OK) == 0) {
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3dd538..9d4ea8c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -18,6 +18,8 @@
 
 #include <fcntl.h>
 #include <poll.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 #include <unistd.h>
 
 #include <android-base/file.h>
@@ -181,6 +183,8 @@
     trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
 
     auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
+    // Restore prio before main loop
+    setpriority(PRIO_PROCESS, 0, 0);
     subcontext_process.MainLoop();
     return 0;
 }
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 7514b61..54659c5 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -321,6 +321,8 @@
     while (waitpid(-1, nullptr, WNOHANG) > 0) {
     }
 
+    // Restore prio before main loop
+    setpriority(PRIO_PROCESS, 0, 0);
     uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
         for (auto& uevent_handler : uevent_handlers) {
             uevent_handler->HandleUevent(uevent);
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index f5e060c..2392112 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -474,36 +474,7 @@
 }
 
 bool __android_logger_valid_buffer_size(unsigned long value) {
-  static long pages, pagesize;
-  unsigned long maximum;
-
-  if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
-    return false;
-  }
-
-  if (!pages) {
-    pages = sysconf(_SC_PHYS_PAGES);
-  }
-  if (pages < 1) {
-    return true;
-  }
-
-  if (!pagesize) {
-    pagesize = sysconf(_SC_PAGESIZE);
-    if (pagesize <= 1) {
-      pagesize = PAGE_SIZE;
-    }
-  }
-
-  /* maximum memory impact a somewhat arbitrary ~3% */
-  pages = (pages + 31) / 32;
-  maximum = pages * pagesize;
-
-  if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
-    return true;
-  }
-
-  return value <= maximum;
+  return LOG_BUFFER_MIN_SIZE <= value && value <= LOG_BUFFER_MAX_SIZE;
 }
 
 struct cache2_property_size {
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 268496f..65371fa 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -23,6 +23,21 @@
     export_include_dirs: ["include"],
 }
 
+cc_library_static {
+    name: "libipchecksum",
+
+    srcs: [
+        "checksum.c",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
 cc_binary {
     name: "dhcpdbg",
 
diff --git a/libpixelflinger/include/pixelflinger/format.h b/libpixelflinger/include/pixelflinger/format.h
index 82eeca4..d429477 100644
--- a/libpixelflinger/include/pixelflinger/format.h
+++ b/libpixelflinger/include/pixelflinger/format.h
@@ -82,7 +82,7 @@
     GGL_INDEX_CR      = 2,
 };
 
-typedef struct {
+typedef struct GGLFormat {
 #ifdef __cplusplus
     enum {
         ALPHA   = GGL_INDEX_ALPHA,
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 19c22c8..8cc780a 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -341,6 +341,37 @@
 }
 
 //-------------------------------------------------------------------------
+// Fuzzers
+//-------------------------------------------------------------------------
+cc_defaults {
+    name: "libunwindstack_fuzz_defaults",
+    host_supported: true,
+    defaults: ["libunwindstack_flags"],
+    cflags: [
+        "-Wno-exit-time-destructors",
+        "-g",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "liblzma",
+        "libunwindstack",
+        "libdexfile_support",
+    ],
+}
+
+cc_fuzz {
+    name: "libunwindstack_fuzz_unwinder",
+    defaults: ["libunwindstack_fuzz_defaults"],
+    srcs: [
+        "tests/MemoryFake.cpp",
+        "tests/ElfFake.cpp",
+        "tests/fuzz/UnwinderComponentCreator.cpp",
+        "tests/fuzz/UnwinderFuzz.cpp",
+    ],
+}
+
+//-------------------------------------------------------------------------
 // Tools
 //-------------------------------------------------------------------------
 cc_defaults {
@@ -458,3 +489,4 @@
         "tests/GenGnuDebugdata.cpp",
     ],
 }
+
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
new file mode 100644
index 0000000..94f5a73
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright 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 "UnwinderComponentCreator.h"
+
+std::unique_ptr<Regs> GetRegisters(ArchEnum arch) {
+  switch (arch) {
+    case unwindstack::ARCH_ARM: {
+      std::unique_ptr<unwindstack::RegsArm> regs = std::make_unique<unwindstack::RegsArm>();
+      return regs;
+    }
+    case unwindstack::ARCH_ARM64: {
+      std::unique_ptr<unwindstack::RegsArm64> regs = std::make_unique<unwindstack::RegsArm64>();
+      return regs;
+    }
+    case unwindstack::ARCH_X86: {
+      std::unique_ptr<unwindstack::RegsX86> regs = std::make_unique<unwindstack::RegsX86>();
+      return regs;
+    }
+    case unwindstack::ARCH_X86_64: {
+      std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+      return regs;
+    }
+    case unwindstack::ARCH_MIPS: {
+      std::unique_ptr<unwindstack::RegsMips> regs = std::make_unique<unwindstack::RegsMips>();
+      return regs;
+    }
+    case unwindstack::ARCH_MIPS64: {
+      std::unique_ptr<unwindstack::RegsMips64> regs = std::make_unique<unwindstack::RegsMips64>();
+      return regs;
+    }
+    case unwindstack::ARCH_UNKNOWN:
+    default: {
+      std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+      return regs;
+    }
+  }
+}
+
+ArchEnum GetArch(FuzzedDataProvider* data_provider) {
+  uint8_t arch = data_provider->ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+  return static_cast<ArchEnum>(arch);
+}
+
+void ElfAddMapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+                   const char* name, Elf* elf = nullptr) {
+  std::string str_name(name);
+  maps->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+  if (elf != nullptr) {
+    const auto& map_info = *--maps->end();
+    map_info->elf.reset(elf);
+  }
+}
+
+void ElfPushFakeFunctionData(FuzzedDataProvider* data_provider, ElfInterfaceFake* elf) {
+  uint8_t func_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxFuncCount);
+  for (uint8_t i = 0; i < func_count; i++) {
+    std::string func_name = data_provider->ConsumeRandomLengthString(kMaxFuncNameLen);
+    bool global = data_provider->ConsumeBool();
+    if (global) {
+      elf->FakeSetGlobalVariable(func_name, data_provider->ConsumeIntegral<uint64_t>());
+    } else {
+      ElfInterfaceFake::FakePushFunctionData(FunctionData(func_name, i));
+    }
+  }
+}
+void ElfPushFakeStepData(FuzzedDataProvider* data_provider) {
+  uint8_t step_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxStepCount);
+  for (uint8_t i = 0; i < step_count; i++) {
+    uint64_t pc = data_provider->ConsumeIntegral<uint64_t>();
+    uint64_t sp = data_provider->ConsumeIntegral<uint64_t>();
+    bool finished = i + 1 == step_count;
+    ElfInterfaceFake::FakePushStepData(StepData(pc, sp, finished));
+  }
+}
+
+ElfFake* PopulateElfFake(FuzzedDataProvider* data_provider) {
+  // This will be passed to a smart pointer in ElfAddMapInfo.
+  ElfFake* elf = new ElfFake(new MemoryFake);
+
+  // This will be handled by a smart pointer within Elf.
+  ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+  std::string build_id = data_provider->ConsumeRandomLengthString(kMaxBuildIdLen);
+  interface_fake->FakeSetBuildID(build_id);
+  std::string so_name = data_provider->ConsumeRandomLengthString(kMaxSoNameLen);
+  interface_fake->FakeSetSoname(so_name.c_str());
+
+  elf->FakeSetArch(GetArch(data_provider));
+  elf->FakeSetLoadBias(data_provider->ConsumeIntegral<uint64_t>());
+
+  ElfPushFakeFunctionData(data_provider, interface_fake);
+  ElfPushFakeStepData(data_provider);
+
+  elf->FakeSetInterface(interface_fake);
+  ElfInterfaceFake::FakeClear();
+  return elf;
+}
+
+std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) {
+  std::unique_ptr<Maps> maps = std::make_unique<Maps>();
+  uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount);
+  for (uint8_t i = 0; i < entry_count; i++) {
+    uint64_t start = data_provider->ConsumeIntegral<uint64_t>();
+    uint64_t end = data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX);
+    uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+    std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen);
+    uint8_t flags = PROT_READ | PROT_WRITE;
+
+    bool exec = data_provider->ConsumeBool();
+    if (exec) {
+      flags |= PROT_EXEC;
+    }
+
+    bool shouldAddElf = data_provider->ConsumeBool();
+    if (shouldAddElf) {
+      ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str(),
+                    PopulateElfFake(data_provider));
+    } else {
+      ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str());
+    }
+  }
+  maps->Sort();
+  return maps;
+}
+
+// This code (until PutElfFilesInMemory) is pretty much directly copied from JitDebugTest.cpp
+// There's a few minor modifications, most notably, all methods accept a MemoryFake pointer, and
+// PutElfInMemory inserts JIT data when called.
+void WriteDescriptor32(MemoryFake* memory, uint64_t addr, uint32_t entry) {
+  // Format of the 32 bit JITDescriptor structure:
+  //   uint32_t version
+  memory->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory->SetData32(addr + 4, 0);
+  //   uint32_t relevant_entry
+  memory->SetData32(addr + 8, 0);
+  //   uint32_t first_entry
+  memory->SetData32(addr + 12, entry);
+}
+
+void WriteDescriptor64(MemoryFake* memory, uint64_t addr, uint64_t entry) {
+  // Format of the 64 bit JITDescriptor structure:
+  //   uint32_t version
+  memory->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory->SetData32(addr + 4, 0);
+  //   uint64_t relevant_entry
+  memory->SetData64(addr + 8, 0);
+  //   uint64_t first_entry
+  memory->SetData64(addr + 16, entry);
+}
+
+void WriteEntry32Pack(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+                      uint32_t elf_addr, uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory->SetData32(addr, next);
+  //   uint32_t prev
+  memory->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory->SetData32(addr + 8, elf_addr);
+  //   uint64_t symfile_size
+  memory->SetData64(addr + 12, elf_size);
+}
+
+void WriteEntry32Pad(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+                     uint32_t elf_addr, uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory->SetData32(addr, next);
+  //   uint32_t prev
+  memory->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory->SetData32(addr + 8, elf_addr);
+  //   uint32_t pad
+  memory->SetData32(addr + 12, 0);
+  //   uint64_t symfile_size
+  memory->SetData64(addr + 16, elf_size);
+}
+
+void WriteEntry64(MemoryFake* memory, uint64_t addr, uint64_t prev, uint64_t next,
+                  uint64_t elf_addr, uint64_t elf_size) {
+  // Format of the 64 bit JITCodeEntry structure:
+  //   uint64_t next
+  memory->SetData64(addr, next);
+  //   uint64_t prev
+  memory->SetData64(addr + 8, prev);
+  //   uint64_t symfile_addr
+  memory->SetData64(addr + 16, elf_addr);
+  //   uint64_t symfile_size
+  memory->SetData64(addr + 24, elf_size);
+}
+
+template <typename EhdrType, typename ShdrType>
+void PutElfInMemory(MemoryFake* memory, uint64_t offset, uint8_t class_type, uint8_t machine_type,
+                    uint32_t pc, uint32_t size) {
+  EhdrType ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  uint64_t sh_offset = sizeof(ehdr);
+  memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+  ehdr.e_ident[EI_CLASS] = class_type;
+  ehdr.e_machine = machine_type;
+  ehdr.e_shstrndx = 1;
+  ehdr.e_shoff = sh_offset;
+  ehdr.e_shentsize = sizeof(ShdrType);
+  ehdr.e_shnum = 3;
+  memory->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+  ShdrType shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NULL;
+  memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x500;
+  shdr.sh_size = 0x100;
+  memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+  memory->SetMemory(offset + 0x500, ".debug_frame");
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0;
+  shdr.sh_addr = 0x600;
+  shdr.sh_offset = 0x600;
+  shdr.sh_size = 0x200;
+  memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+  // Now add a single cie/fde.
+  uint64_t dwarf_offset = offset + 0x600;
+  if (class_type == ELFCLASS32) {
+    // CIE 32 information.
+    memory->SetData32(dwarf_offset, 0xfc);
+    memory->SetData32(dwarf_offset + 0x4, 0xffffffff);
+    memory->SetData8(dwarf_offset + 0x8, 1);
+    memory->SetData8(dwarf_offset + 0x9, '\0');
+    memory->SetData8(dwarf_offset + 0xa, 0x4);
+    memory->SetData8(dwarf_offset + 0xb, 0x4);
+    memory->SetData8(dwarf_offset + 0xc, 0x1);
+
+    // FDE 32 information.
+    memory->SetData32(dwarf_offset + 0x100, 0xfc);
+    memory->SetData32(dwarf_offset + 0x104, 0);
+    memory->SetData32(dwarf_offset + 0x108, pc);
+    memory->SetData32(dwarf_offset + 0x10c, size);
+  } else {
+    // CIE 64 information.
+    memory->SetData32(dwarf_offset, 0xffffffff);
+    memory->SetData64(dwarf_offset + 4, 0xf4);
+    memory->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+    memory->SetData8(dwarf_offset + 0x14, 1);
+    memory->SetData8(dwarf_offset + 0x15, '\0');
+    memory->SetData8(dwarf_offset + 0x16, 0x4);
+    memory->SetData8(dwarf_offset + 0x17, 0x4);
+    memory->SetData8(dwarf_offset + 0x18, 0x1);
+
+    // FDE 64 information.
+    memory->SetData32(dwarf_offset + 0x100, 0xffffffff);
+    memory->SetData64(dwarf_offset + 0x104, 0xf4);
+    memory->SetData64(dwarf_offset + 0x10c, 0);
+    memory->SetData64(dwarf_offset + 0x114, pc);
+    memory->SetData64(dwarf_offset + 0x11c, size);
+  }
+}
+
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider) {
+  uint8_t elf_file_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxJitElfFiles);
+  int entry_offset = 0;
+  int prev_jit_addr = 0;
+  for (uint8_t i = 0; i < elf_file_count; i++) {
+    uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+    // Technically the max valid value is ELFCLASSNUM - 1 (2), but
+    // we want to test values outside of that range.
+    uint8_t class_type = data_provider->ConsumeIntegral<uint8_t>();
+    // Same here, EM_NUM is 253, max valid machine type is 252
+    uint8_t machine_type = data_provider->ConsumeIntegral<uint8_t>();
+    uint32_t pc = data_provider->ConsumeIntegral<uint32_t>();
+    uint32_t size = data_provider->ConsumeIntegral<uint32_t>();
+    bool sixty_four_bit = data_provider->ConsumeBool();
+    bool write_jit = data_provider->ConsumeBool();
+    if (sixty_four_bit) {
+      PutElfInMemory<Elf64_Ehdr, Elf64_Shdr>(memory, offset, class_type, machine_type, pc, size);
+    } else {
+      PutElfInMemory<Elf32_Ehdr, Elf32_Shdr>(memory, offset, class_type, machine_type, pc, size);
+    }
+    if (write_jit) {
+      bool use_pad = data_provider->ConsumeBool();
+      // It is possible this will overwrite part of the ELF.
+      // This provides an interesting test of how malformed ELF
+      // data is handled.
+      uint64_t cur_descriptor_addr = 0x11800 + entry_offset;
+      uint64_t cur_jit_addr = 0x200000 + entry_offset;
+      uint64_t next_jit_addr = cur_jit_addr + size;
+      if (sixty_four_bit) {
+        WriteDescriptor64(memory, 0x11800, cur_jit_addr);
+        WriteEntry64(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+      } else {
+        // Loop back. Again, this may corrupt data,
+        // but that will allow for testing edge cases with
+        // malformed JIT data.
+        if (cur_jit_addr > UINT32_MAX) {
+          entry_offset = 0;
+          cur_jit_addr = 0x200000;
+          cur_descriptor_addr = 0x11800;
+          next_jit_addr = cur_jit_addr + size;
+        }
+        WriteDescriptor32(memory, cur_descriptor_addr, cur_jit_addr);
+        if (use_pad) {
+          WriteEntry32Pad(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+        } else {
+          WriteEntry32Pack(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+        }
+      }
+      entry_offset += size;
+      prev_jit_addr = cur_jit_addr;
+    }
+  }
+}
+
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+                                       uint max_strings) {
+  uint str_count = data_provider->ConsumeIntegralInRange<uint>(0, max_strings);
+  std::vector<std::string> strings;
+  for (uint i = 0; i < str_count; i++) {
+    strings.push_back(data_provider->ConsumeRandomLengthString(max_str_len));
+  }
+  return strings;
+}
+
+std::unique_ptr<DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+                                      std::shared_ptr<Memory> memory, uint max_library_length,
+                                      uint max_libraries) {
+  std::vector<std::string> search_libs =
+      GetStringList(data_provider, max_library_length, max_libraries);
+  if (search_libs.size() <= 0) {
+    return std::make_unique<DexFiles>(memory);
+  }
+
+  return std::make_unique<DexFiles>(memory, search_libs);
+}
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
new file mode 100644
index 0000000..09b3379
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+#define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+
+#include <elf.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+#include "../ElfFake.h"
+#include "../MemoryFake.h"
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using unwindstack::ArchEnum;
+using unwindstack::DexFiles;
+using unwindstack::Elf;
+using unwindstack::ElfFake;
+using unwindstack::ElfInterfaceFake;
+using unwindstack::FunctionData;
+using unwindstack::Maps;
+using unwindstack::Memory;
+using unwindstack::MemoryFake;
+using unwindstack::Regs;
+using unwindstack::StepData;
+
+static constexpr uint8_t kArchCount = 6;
+
+static constexpr uint8_t kMaxSoNameLen = 150;
+
+static constexpr uint8_t kMaxFuncNameLen = 50;
+static constexpr uint8_t kMaxFuncCount = 100;
+
+static constexpr uint8_t kMaxJitElfFiles = 20;
+static constexpr uint8_t kJitElfPadding = 32;
+
+static constexpr uint8_t kMaxStepCount = 100;
+static constexpr uint8_t kMaxMapEntryCount = 50;
+static constexpr uint8_t kMaxBuildIdLen = 100;
+static constexpr uint8_t kMaxMapInfoNameLen = 150;
+
+std::unique_ptr<unwindstack::Regs> GetRegisters(unwindstack::ArchEnum arch);
+std::unique_ptr<unwindstack::Maps> GetMaps(FuzzedDataProvider* data_provider);
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+                                       uint max_strings);
+unwindstack::ArchEnum GetArch(FuzzedDataProvider* data_provider);
+
+void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name,
+                Elf* elf = nullptr);
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider);
+
+std::unique_ptr<unwindstack::DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+                                                   std::shared_ptr<unwindstack::Memory> memory,
+                                                   uint max_libraries, uint max_library_length);
+#endif  // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
new file mode 100644
index 0000000..2f4986a
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 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 <functional>
+#include <iostream>
+#include <vector>
+
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Unwinder.h>
+
+#include "../MemoryFake.h"
+#include "UnwinderComponentCreator.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+namespace unwindstack {
+
+static constexpr int kMaxUnwindStringLen = 50;
+static constexpr int kMaxUnwindStrings = 50;
+
+void PerformUnwind(FuzzedDataProvider* data_provider, Unwinder* unwinder) {
+  // 0 = don't set any values
+  // 1 = set initial_map_names_to_skip
+  // 2 = set map_suffixes_to_ignore
+  // 3 = set both
+  uint8_t set_values = data_provider->ConsumeIntegral<uint8_t>() % 4;
+  if (set_values == 0) {
+    unwinder->Unwind();
+  } else if (set_values == 1) {
+    // Only setting initial_map_names_to_skip
+    std::vector<std::string> skip_names =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+    unwinder->Unwind(&skip_names, nullptr);
+  } else if (set_values == 2) {
+    // Only setting map_suffixes_to_ignore
+    std::vector<std::string> ignore_suffixes =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+    unwinder->Unwind(nullptr, &ignore_suffixes);
+  } else if (set_values == 3) {
+    // Setting both values
+    std::vector<std::string> skip_names =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+    std::vector<std::string> ignore_suffixes =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+    unwinder->Unwind(&skip_names, &ignore_suffixes);
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+
+  // We need to construct an unwinder.
+  // Generate the Maps:
+  std::unique_ptr<Maps> maps = GetMaps(&data_provider);
+
+  // Generate the Regs:
+  uint8_t arch_val = data_provider.ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+  ArchEnum arch = static_cast<ArchEnum>(arch_val);
+  std::unique_ptr<Regs> regs = GetRegisters(arch);
+
+  // Generate memory:
+  std::shared_ptr<Memory> memory = std::make_shared<MemoryFake>();
+  PutElfFilesInMemory(reinterpret_cast<MemoryFake*>(memory.get()), &data_provider);
+
+  size_t max_frames = data_provider.ConsumeIntegralInRange<size_t>(0, 5000);
+
+  std::unique_ptr<JitDebug> jit_debug_ptr = std::make_unique<JitDebug>(memory);
+
+  // Create instance
+  Unwinder unwinder(max_frames, maps.get(), regs.get(), memory);
+  unwinder.SetJitDebug(jit_debug_ptr.get(), arch);
+  unwinder.SetResolveNames(data_provider.ConsumeBool());
+  // Call unwind
+  PerformUnwind(&data_provider, &unwinder);
+
+  // Run some additional logic that changes after unwind
+  uint64_t pc = data_provider.ConsumeIntegral<uint64_t>();
+  unwinder.BuildFrameFromPcOnly(pc);
+  unwinder.ConsumeFrames();
+  return 0;
+}
+}  // namespace unwindstack
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 7392806..022dedf 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -78,6 +78,9 @@
         "libcutils",
         "liblog",
     ],
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
 
     target: {
         android: {
@@ -245,6 +248,7 @@
         "LruCache_test.cpp",
         "Mutex_test.cpp",
         "SharedBuffer_test.cpp",
+        "Singleton_test.cpp",
         "String8_test.cpp",
         "String16_test.cpp",
         "StrongPointer_test.cpp",
@@ -281,6 +285,11 @@
         },
     },
 
+    data_libs: [
+        "libutils_test_singleton1",
+        "libutils_test_singleton2",
+    ],
+
     cflags: [
         "-Wall",
         "-Wextra",
@@ -291,29 +300,10 @@
     test_suites: ["device-tests"],
 }
 
-// TODO: the test infrastructure isn't yet capable of running this,
-// so it's broken out into its own test so that the main libutils_tests
-// can be in presubmit even if this can't.
-
-cc_test {
-    name: "libutils_singleton_test",
-    srcs: ["Singleton_test.cpp"],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: ["libbase"],
-
-    required: [
-        ":libutils_test_singleton1",
-        ":libutils_test_singleton2",
-    ],
-}
-
 cc_test_library {
     name: "libutils_test_singleton1",
     host_supported: true,
-    relative_install_path: "libutils_test",
+    installable: false,
     srcs: ["Singleton_test1.cpp"],
     cflags: [
         "-Wall",
@@ -324,7 +314,7 @@
 cc_test_library {
     name: "libutils_test_singleton2",
     host_supported: true,
-    relative_install_path: "libutils_test",
+    installable: false,
     srcs: ["Singleton_test2.cpp"],
     cflags: [
         "-Wall",
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index d00e39c..c837891 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -424,7 +424,7 @@
     char* buf = lockBuffer(len);
     buf += start;
     while (length > 0) {
-        *buf = tolower(*buf);
+        *buf = static_cast<char>(tolower(*buf));
         buf++;
         length--;
     }
@@ -448,7 +448,7 @@
     char* buf = lockBuffer(len);
     buf += start;
     while (length > 0) {
-        *buf = toupper(*buf);
+        *buf = static_cast<char>(toupper(*buf));
         buf++;
         length--;
     }
diff --git a/libutils/include/utils/BitSet.h b/libutils/include/utils/BitSet.h
index 8abfb1a..0fb1a6a 100644
--- a/libutils/include/utils/BitSet.h
+++ b/libutils/include/utils/BitSet.h
@@ -21,9 +21,12 @@
 #include <utils/TypeHelpers.h>
 
 /*
- * Contains some bit manipulation helpers.
+ * A class to provide efficient manipulation of bitsets.
  *
- * DO NOT USE: std::bitset<32> or std::bitset<64> preferred
+ * Consider using std::bitset<32> or std::bitset<64> if all you want is a class to do basic bit
+ * manipulation (i.e. AND / OR / XOR / flip / etc). These classes are only needed if you want to
+ * efficiently perform operations like finding the first set bit in a bitset and you want to
+ * avoid using the built-in functions (e.g. __builtin_clz) on std::bitset::to_ulong.
  */
 
 namespace android {
@@ -46,7 +49,9 @@
     // Returns the number of marked bits in the set.
     inline uint32_t count() const { return count(value); }
 
-    static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); }
+    static inline uint32_t count(uint32_t value) {
+        return static_cast<uint32_t>(__builtin_popcountl(value));
+    }
 
     // Returns true if the bit set does not contain any marked bits.
     inline bool isEmpty() const { return isEmpty(value); }
@@ -128,7 +133,7 @@
     }
 
     static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {
-        return __builtin_popcountl(value & ~(0xffffffffUL >> n));
+        return static_cast<uint32_t>(__builtin_popcountl(value & ~(0xffffffffUL >> n)));
     }
 
     inline bool operator== (const BitSet32& other) const { return value == other.value; }
@@ -153,17 +158,17 @@
     // input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.
     static inline uint32_t clz_checked(uint32_t value) {
         if (sizeof(unsigned int) == sizeof(uint32_t)) {
-            return __builtin_clz(value);
+            return static_cast<uint32_t>(__builtin_clz(value));
         } else {
-            return __builtin_clzl(value);
+            return static_cast<uint32_t>(__builtin_clzl(value));
         }
     }
 
     static inline uint32_t ctz_checked(uint32_t value) {
         if (sizeof(unsigned int) == sizeof(uint32_t)) {
-            return __builtin_ctz(value);
+            return static_cast<uint32_t>(__builtin_ctz(value));
         } else {
-            return __builtin_ctzl(value);
+            return static_cast<uint32_t>(__builtin_ctzl(value));
         }
     }
 };
@@ -188,7 +193,9 @@
     // Returns the number of marked bits in the set.
     inline uint32_t count() const { return count(value); }
 
-    static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); }
+    static inline uint32_t count(uint64_t value) {
+        return static_cast<uint32_t>(__builtin_popcountll(value));
+    }
 
     // Returns true if the bit set does not contain any marked bits.
     inline bool isEmpty() const { return isEmpty(value); }
@@ -219,26 +226,32 @@
     // Result is undefined if all bits are unmarked.
     inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
 
-    static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); }
+    static inline uint32_t firstMarkedBit(uint64_t value) {
+        return static_cast<uint32_t>(__builtin_clzll(value));
+    }
 
     // Finds the first unmarked bit in the set.
     // Result is undefined if all bits are marked.
     inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
 
-    static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); }
+    static inline uint32_t firstUnmarkedBit(uint64_t value) {
+        return static_cast<uint32_t>(__builtin_clzll(~value));
+    }
 
     // Finds the last marked bit in the set.
     // Result is undefined if all bits are unmarked.
     inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
 
-    static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); }
+    static inline uint32_t lastMarkedBit(uint64_t value) {
+        return static_cast<uint32_t>(63 - __builtin_ctzll(value));
+    }
 
     // Finds the first marked bit in the set and clears it.  Returns the bit index.
     // Result is undefined if all bits are unmarked.
     inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
 
     static inline uint32_t clearFirstMarkedBit(uint64_t& value) {
-        uint64_t n = firstMarkedBit(value);
+        uint32_t n = firstMarkedBit(value);
         clearBit(value, n);
         return n;
     }
@@ -248,7 +261,7 @@
     inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
 
     static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {
-        uint64_t n = firstUnmarkedBit(value);
+        uint32_t n = firstUnmarkedBit(value);
         markBit(value, n);
         return n;
     }
@@ -258,7 +271,7 @@
     inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
 
     static inline uint32_t clearLastMarkedBit(uint64_t& value) {
-        uint64_t n = lastMarkedBit(value);
+        uint32_t n = lastMarkedBit(value);
         clearBit(value, n);
         return n;
     }
@@ -268,7 +281,7 @@
     inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); }
 
     static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) {
-        return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n));
+        return static_cast<uint32_t>(__builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)));
     }
 
     inline bool operator== (const BitSet64& other) const { return value == other.value; }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index dd779f9..26affa8 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -99,6 +99,10 @@
 }
 
 LogStatisticsElement LogBufferElement::ToLogStatisticsElement() const {
+    // Estimate the size of this element in the parent std::list<> by adding two void*'s
+    // corresponding to the next/prev pointers and aligning to 64 bit.
+    uint16_t element_in_list_size =
+            (sizeof(*this) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) & -sizeof(uint64_t);
     return LogStatisticsElement{
             .uid = uid(),
             .pid = pid(),
@@ -109,6 +113,7 @@
             .msg_len = msg_len(),
             .dropped_count = dropped_count(),
             .log_id = log_id(),
+            .total_len = static_cast<uint16_t>(element_in_list_size + msg_len()),
     };
 }
 
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index 5d57ad1..1fd22c2 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -87,6 +87,6 @@
     LogReaderList reader_list_;
     LogTags tags_;
     PruneList prune_;
-    LogStatistics stats_{false};
+    LogStatistics stats_{false, true};
     std::unique_ptr<LogBuffer> log_buffer_;
 };
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 3cd8fde..1705d48 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -27,6 +27,7 @@
 
 #include <list>
 
+#include <android-base/logging.h>
 #include <private/android_logger.h>
 
 #include "LogBufferElement.h"
@@ -60,7 +61,8 @@
     return std::string(msg, len);
 }
 
-LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
+LogStatistics::LogStatistics(bool enable_statistics, bool track_total_size)
+    : enable(enable_statistics), track_total_size_(track_total_size) {
     log_time now(CLOCK_REALTIME);
     log_id_for_each(id) {
         mSizes[id] = 0;
@@ -113,10 +115,15 @@
     ++mElementsTotal[log_id];
 }
 
-void LogStatistics::Add(const LogStatisticsElement& element) {
+void LogStatistics::Add(LogStatisticsElement element) {
     auto lock = std::lock_guard{lock_};
+
+    if (!track_total_size_) {
+        element.total_len = element.msg_len;
+    }
+
     log_id_t log_id = element.log_id;
-    uint16_t size = element.msg_len;
+    uint16_t size = element.total_len;
     mSizes[log_id] += size;
     ++mElements[log_id];
 
@@ -184,10 +191,15 @@
     }
 }
 
-void LogStatistics::Subtract(const LogStatisticsElement& element) {
+void LogStatistics::Subtract(LogStatisticsElement element) {
     auto lock = std::lock_guard{lock_};
+
+    if (!track_total_size_) {
+        element.total_len = element.msg_len;
+    }
+
     log_id_t log_id = element.log_id;
-    uint16_t size = element.msg_len;
+    uint16_t size = element.total_len;
     mSizes[log_id] -= size;
     --mElements[log_id];
     if (element.dropped_count) {
@@ -230,7 +242,9 @@
 
 // Atomically set an entry to drop
 // entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::Drop(const LogStatisticsElement& element) {
+void LogStatistics::Drop(LogStatisticsElement element) {
+    CHECK_EQ(element.dropped_count, 0U);
+
     auto lock = std::lock_guard{lock_};
     log_id_t log_id = element.log_id;
     uint16_t size = element.msg_len;
@@ -265,6 +279,43 @@
     tagNameTable.Subtract(TagNameKey(element), element);
 }
 
+void LogStatistics::Erase(LogStatisticsElement element) {
+    CHECK_GT(element.dropped_count, 0U);
+    CHECK_EQ(element.msg_len, 0U);
+
+    auto lock = std::lock_guard{lock_};
+
+    if (!track_total_size_) {
+        element.total_len = 0;
+    }
+
+    log_id_t log_id = element.log_id;
+    --mElements[log_id];
+    --mDroppedElements[log_id];
+    mSizes[log_id] -= element.total_len;
+
+    uidTable[log_id].Erase(element.uid, element);
+    if (element.uid == AID_SYSTEM) {
+        pidSystemTable[log_id].Erase(element.pid, element);
+    }
+
+    if (!enable) {
+        return;
+    }
+
+    pidTable.Erase(element.pid, element);
+    tidTable.Erase(element.tid, element);
+
+    uint32_t tag = element.tag;
+    if (tag) {
+        if (log_id == LOG_ID_SECURITY) {
+            securityTagTable.Erase(tag, element);
+        } else {
+            tagTable.Erase(tag, element);
+        }
+    }
+}
+
 const char* LogStatistics::UidToName(uid_t uid) const {
     auto lock = std::lock_guard{lock_};
     return UidToNameLocked(uid);
@@ -876,12 +927,20 @@
         if (els) {
             oldLength = output.length();
             if (spaces < 0) spaces = 0;
-            // estimate the std::list overhead.
-            static const size_t overhead =
-                ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
-                 -sizeof(uint64_t)) +
-                sizeof(std::list<LogBufferElement*>);
-            size_t szs = mSizes[id] + els * overhead;
+            size_t szs = 0;
+            if (overhead_[id]) {
+                szs = *overhead_[id];
+            } else if (track_total_size_) {
+                szs = mSizes[id];
+            } else {
+                // Legacy fallback for Chatty without track_total_size_
+                // Estimate the size of this element in the parent std::list<> by adding two void*'s
+                // corresponding to the next/prev pointers and aligning to 64 bit.
+                static const size_t overhead =
+                        (sizeof(LogBufferElement) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) &
+                        -sizeof(uint64_t);
+                szs = mSizes[id] + els * overhead;
+            }
             totalSize += szs;
             output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
             spaces -= output.length() - oldLength;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 5f55802..d440a8c 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -57,6 +57,7 @@
     uint16_t msg_len;
     uint16_t dropped_count;
     log_id_t log_id;
+    uint16_t total_len;
 };
 
 template <typename TKey, typename TEntry>
@@ -172,6 +173,13 @@
         }
     }
 
+    void Erase(const TKey& key, const LogStatisticsElement& element) {
+        iterator it = map.find(key);
+        if (it != map.end()) {
+            it->second.Erase(element);
+        }
+    }
+
     iterator begin() { return map.begin(); }
     const_iterator begin() const { return map.begin(); }
     iterator end() { return map.end(); }
@@ -181,15 +189,17 @@
 class EntryBase {
   public:
     EntryBase() : size_(0) {}
-    explicit EntryBase(const LogStatisticsElement& element) : size_(element.msg_len) {}
+    explicit EntryBase(const LogStatisticsElement& element) : size_(element.total_len) {}
 
     size_t getSizes() const { return size_; }
 
-    void Add(const LogStatisticsElement& element) { size_ += element.msg_len; }
+    void Add(const LogStatisticsElement& element) { size_ += element.total_len; }
     bool Subtract(const LogStatisticsElement& element) {
-        size_ -= element.msg_len;
-        return !size_;
+        size_ -= element.total_len;
+        return size_ == 0;
     }
+    void Drop(const LogStatisticsElement& element) { size_ -= element.msg_len; }
+    void Erase(const LogStatisticsElement& element) { size_ -= element.total_len; }
 
     static constexpr size_t PRUNED_LEN = 14;
     static constexpr size_t TOTAL_LEN = 80;
@@ -229,11 +239,11 @@
     }
     bool Subtract(const LogStatisticsElement& element) {
         dropped_ -= element.dropped_count;
-        return EntryBase::Subtract(element) && !dropped_;
+        return EntryBase::Subtract(element) && dropped_ == 0;
     }
     void Drop(const LogStatisticsElement& element) {
         dropped_ += 1;
-        EntryBase::Subtract(element);
+        EntryBase::Drop(element);
     }
 
   private:
@@ -505,20 +515,23 @@
     }
 
   public:
-    explicit LogStatistics(bool enable_statistics);
+    LogStatistics(bool enable_statistics, bool track_total_size);
 
     void AddTotal(log_id_t log_id, uint16_t size) EXCLUDES(lock_);
-    void Add(const LogStatisticsElement& entry) EXCLUDES(lock_);
-    void Subtract(const LogStatisticsElement& entry) EXCLUDES(lock_);
-    // entry->setDropped(1) must follow this call
-    void Drop(const LogStatisticsElement& entry) EXCLUDES(lock_);
-    // Correct for coalescing two entries referencing dropped content
-    void Erase(const LogStatisticsElement& element) EXCLUDES(lock_) {
-        auto lock = std::lock_guard{lock_};
-        log_id_t log_id = element.log_id;
-        --mElements[log_id];
-        --mDroppedElements[log_id];
-    }
+
+    // Add is for adding an element to the log buffer.  It may be a chatty element in the case of
+    // log deduplication.  Add the total size of the element to statistics.
+    void Add(LogStatisticsElement entry) EXCLUDES(lock_);
+    // Subtract is for removing an element from the log buffer.  It may be a chatty element.
+    // Subtract the total size of the element from statistics.
+    void Subtract(LogStatisticsElement entry) EXCLUDES(lock_);
+    // Drop is for converting a normal element into a chatty element. entry->setDropped(1) must
+    // follow this call.  Subtract only msg_len from statistics, since a chatty element will remain.
+    void Drop(LogStatisticsElement entry) EXCLUDES(lock_);
+    // Erase is for coalescing two chatty elements into one.  Erase() is called on the element that
+    // is removed from the log buffer.  Subtract the total size of the element, which is by
+    // definition only the size of the LogBufferElement + list overhead for chatty elements.
+    void Erase(LogStatisticsElement element) EXCLUDES(lock_);
 
     void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
                       size_t* second_worst_sizes) const EXCLUDES(lock_);
@@ -533,6 +546,9 @@
     // Snapshot of the sizes for a given log buffer.
     size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
         auto lock = std::lock_guard{lock_};
+        if (overhead_[id]) {
+            return *overhead_[id];
+        }
         return mSizes[id];
     }
     // TODO: Get rid of this entirely.
@@ -546,6 +562,11 @@
     uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
     const char* UidToName(uid_t uid) const EXCLUDES(lock_);
 
+    void set_overhead(log_id_t id, size_t size) {
+        auto lock = std::lock_guard{lock_};
+        overhead_[id] = size;
+    }
+
   private:
     template <typename TKey, typename TEntry>
     void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
@@ -559,4 +580,7 @@
     const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
 
     mutable std::mutex lock_;
+    bool track_total_size_;
+
+    std::optional<size_t> overhead_[LOG_ID_MAX] GUARDED_BY(lock_);
 };
diff --git a/logd/README.property b/logd/README.property
index ab9c4d4..1d7d990 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -52,6 +52,9 @@
 log.tag.<tag>             string persist The <tag> specific logging level.
 persist.log.tag.<tag>      string build  default for log.tag.<tag>
 
+logd.buffer_type           string (empty) Set the log buffer type.  Current choices are 'simple',
+                                          'chatty', or 'serialized'.  Defaults to 'chatty' if empty.
+
 NB:
 - auto - managed by /init
 - bool+ - "true", "false" and comma separated list of "eng" (forced false if
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index 0f30794..a626d30 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -32,12 +32,6 @@
     Init();
 }
 
-SerializedLogBuffer::~SerializedLogBuffer() {
-    if (deleter_thread_.joinable()) {
-        deleter_thread_.join();
-    }
-}
-
 void SerializedLogBuffer::Init() {
     log_id_for_each(i) {
         if (SetSize(i, __android_logger_get_buffer_size(i))) {
@@ -109,69 +103,27 @@
 }
 
 void SerializedLogBuffer::MaybePrune(log_id_t log_id) {
-    auto get_total_size = [](const auto& buffer) {
-        size_t total_size = 0;
-        for (const auto& chunk : buffer) {
-            total_size += chunk.PruneSize();
-        }
-        return total_size;
-    };
-    size_t total_size = get_total_size(logs_[log_id]);
+    size_t total_size = GetSizeUsed(log_id);
+    size_t after_size = total_size;
     if (total_size > max_size_[log_id]) {
         Prune(log_id, total_size - max_size_[log_id], 0);
+        after_size = GetSizeUsed(log_id);
         LOG(INFO) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
-                  << " after size: " << get_total_size(logs_[log_id]);
+                  << " after size: " << after_size;
     }
+
+    stats_->set_overhead(log_id, after_size);
 }
 
-void SerializedLogBuffer::StartDeleterThread() {
-    if (deleter_thread_running_) {
-        return;
+void SerializedLogBuffer::RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk) {
+    chunk.IncReaderRefCount();
+    int read_offset = 0;
+    while (read_offset < chunk.write_offset()) {
+        auto* entry = chunk.log_entry(read_offset);
+        stats_->Subtract(entry->ToLogStatisticsElement(log_id));
+        read_offset += entry->total_len();
     }
-    if (deleter_thread_.joinable()) {
-        deleter_thread_.join();
-    }
-    auto new_thread = std::thread([this] { DeleterThread(); });
-    deleter_thread_.swap(new_thread);
-    deleter_thread_running_ = true;
-}
-
-// Decompresses the chunks, call LogStatistics::Subtract() on each entry, then delete the chunks and
-// the list.  Note that the SerializedLogChunk objects have been removed from logs_ and their
-// references have been deleted from any SerializedFlushToState objects, so this can be safely done
-// without holding lock_.  It is done in a separate thread to avoid delaying the writer thread.
-void SerializedLogBuffer::DeleterThread() {
-    prctl(PR_SET_NAME, "logd.deleter");
-    while (true) {
-        std::list<SerializedLogChunk> local_chunks_to_delete;
-        log_id_t log_id;
-        {
-            auto lock = std::lock_guard{lock_};
-            log_id_for_each(i) {
-                if (!chunks_to_delete_[i].empty()) {
-                    local_chunks_to_delete = std::move(chunks_to_delete_[i]);
-                    chunks_to_delete_[i].clear();
-                    log_id = i;
-                    break;
-                }
-            }
-            if (local_chunks_to_delete.empty()) {
-                deleter_thread_running_ = false;
-                return;
-            }
-        }
-
-        for (auto& chunk : local_chunks_to_delete) {
-            chunk.IncReaderRefCount();
-            int read_offset = 0;
-            while (read_offset < chunk.write_offset()) {
-                auto* entry = chunk.log_entry(read_offset);
-                stats_->Subtract(entry->ToLogStatisticsElement(log_id));
-                read_offset += entry->total_len();
-            }
-            chunk.DecReaderRefCount(false);
-        }
-    }
+    chunk.DecReaderRefCount(false);
 }
 
 void SerializedLogBuffer::NotifyReadersOfPrune(
@@ -197,8 +149,6 @@
         }
     }
 
-    StartDeleterThread();
-
     auto& log_buffer = logs_[log_id];
     auto it = log_buffer.begin();
     while (it != log_buffer.end()) {
@@ -206,7 +156,7 @@
             break;
         }
 
-        // Increment ahead of time since we're going to splice this iterator from the list.
+        // Increment ahead of time since we're going to erase this iterator from the list.
         auto it_to_prune = it++;
 
         // The sequence number check ensures that all readers have read all logs in this chunk, but
@@ -222,8 +172,8 @@
             }
         } else {
             size_t buffer_size = it_to_prune->PruneSize();
-            chunks_to_delete_[log_id].splice(chunks_to_delete_[log_id].end(), log_buffer,
-                                             it_to_prune);
+            RemoveChunkFromStats(log_id, *it_to_prune);
+            log_buffer.erase(it_to_prune);
             if (buffer_size >= bytes_to_free) {
                 return true;
             }
@@ -349,19 +299,22 @@
     return Prune(id, ULONG_MAX, uid);
 }
 
+unsigned long SerializedLogBuffer::GetSizeUsed(log_id_t id) {
+    size_t total_size = 0;
+    for (const auto& chunk : logs_[id]) {
+        total_size += chunk.PruneSize();
+    }
+    return total_size;
+}
+
 unsigned long SerializedLogBuffer::GetSize(log_id_t id) {
     auto lock = std::lock_guard{lock_};
-    size_t retval = 2 * max_size_[id] / 3;  // See the comment in SetSize().
-    return retval;
+    return max_size_[id];
 }
 
 // New SerializedLogChunk objects will be allocated according to the new size, but older one are
 // unchanged.  MaybePrune() is called on the log buffer to reduce it to an appropriate size if the
 // new size is lower.
-// ChattyLogBuffer/SimpleLogBuffer don't consider the 'Overhead' of LogBufferElement or the
-// std::list<> overhead as part of the log size.  SerializedLogBuffer does by its very nature, so
-// the 'size' metric is not compatible between them.  Their actual memory usage is between 1.5x and
-// 2x of what they claim to use, so we conservatively set our internal size as size + size / 2.
 int SerializedLogBuffer::SetSize(log_id_t id, unsigned long size) {
     // Reasonable limits ...
     if (!__android_logger_valid_buffer_size(size)) {
@@ -369,7 +322,7 @@
     }
 
     auto lock = std::lock_guard{lock_};
-    max_size_[id] = size + size / 2;
+    max_size_[id] = size;
 
     MaybePrune(id);
 
diff --git a/logd/SerializedLogBuffer.h b/logd/SerializedLogBuffer.h
index 421d419..a03050e 100644
--- a/logd/SerializedLogBuffer.h
+++ b/logd/SerializedLogBuffer.h
@@ -37,7 +37,6 @@
 class SerializedLogBuffer final : public LogBuffer {
   public:
     SerializedLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats);
-    ~SerializedLogBuffer();
     void Init() override;
 
     int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
@@ -61,9 +60,8 @@
             REQUIRES_SHARED(lock_);
     void NotifyReadersOfPrune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk)
             REQUIRES(reader_list_->reader_threads_lock());
-
-    void StartDeleterThread() REQUIRES(lock_);
-    void DeleterThread();
+    void RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk);
+    unsigned long GetSizeUsed(log_id_t id) REQUIRES(lock_);
 
     LogReaderList* reader_list_;
     LogTags* tags_;
@@ -73,9 +71,5 @@
     std::list<SerializedLogChunk> logs_[LOG_ID_MAX] GUARDED_BY(lock_);
     RwLock lock_;
 
-    std::list<SerializedLogChunk> chunks_to_delete_[LOG_ID_MAX] GUARDED_BY(lock_);
-    std::thread deleter_thread_ GUARDED_BY(lock_);
-    bool deleter_thread_running_ GUARDED_BY(lock_) = false;
-
     std::atomic<uint64_t> sequence_ = 1;
 };
diff --git a/logd/SerializedLogChunk.h b/logd/SerializedLogChunk.h
index 65a1e86..50bae63 100644
--- a/logd/SerializedLogChunk.h
+++ b/logd/SerializedLogChunk.h
@@ -44,7 +44,9 @@
     // If this buffer has been compressed, we only consider its compressed size when accounting for
     // memory consumption for pruning.  This is since the uncompressed log is only by used by
     // readers, and thus not a representation of how much these logs cost to keep in memory.
-    size_t PruneSize() const { return compressed_log_.size() ?: contents_.size(); }
+    size_t PruneSize() const {
+        return sizeof(*this) + (compressed_log_.size() ?: contents_.size());
+    }
 
     void FinishWriting() {
         writer_active_ = false;
diff --git a/logd/SerializedLogChunkTest.cpp b/logd/SerializedLogChunkTest.cpp
index 6572503..2b478a3 100644
--- a/logd/SerializedLogChunkTest.cpp
+++ b/logd/SerializedLogChunkTest.cpp
@@ -27,7 +27,7 @@
 TEST(SerializedLogChunk, smoke) {
     size_t chunk_size = 10 * 4096;
     auto chunk = SerializedLogChunk{chunk_size};
-    EXPECT_EQ(chunk_size, chunk.PruneSize());
+    EXPECT_EQ(chunk_size + sizeof(SerializedLogChunk), chunk.PruneSize());
 
     static const char log_message[] = "log message";
     size_t expected_total_len = sizeof(SerializedLogEntry) + sizeof(log_message);
@@ -58,7 +58,7 @@
     size_t individual_message_size = sizeof(SerializedLogEntry) + sizeof(log_message);
     size_t chunk_size = individual_message_size * 3;
     auto chunk = SerializedLogChunk{chunk_size};
-    EXPECT_EQ(chunk_size, chunk.PruneSize());
+    EXPECT_EQ(chunk_size + sizeof(SerializedLogChunk), chunk.PruneSize());
 
     ASSERT_TRUE(chunk.CanLog(individual_message_size));
     EXPECT_NE(nullptr, chunk.Log(1, log_time(), 1000, 1, 1, log_message, sizeof(log_message)));
diff --git a/logd/SerializedLogEntry.h b/logd/SerializedLogEntry.h
index f599abe..2f2c244 100644
--- a/logd/SerializedLogEntry.h
+++ b/logd/SerializedLogEntry.h
@@ -59,6 +59,7 @@
                 .msg_len = msg_len(),
                 .dropped_count = 0,
                 .log_id = log_id,
+                .total_len = total_len(),
         };
     }
 
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 1dc996c..8309f95 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -114,7 +114,7 @@
     LogReaderList reader_list;
     LogTags tags;
     PruneList prune_list;
-    LogStatistics stats(true);
+    LogStatistics stats(true, true);
     std::unique_ptr<LogBuffer> log_buffer;
 #ifdef FUZZ_SERIALIZED
     log_buffer.reset(new SerializedLogBuffer(&reader_list, &tags, &stats));
diff --git a/logd/main.cpp b/logd/main.cpp
index e1ec52b..897e11e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -38,6 +38,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
@@ -61,6 +62,8 @@
 #include "SerializedLogBuffer.h"
 #include "SimpleLogBuffer.h"
 
+using android::base::GetProperty;
+
 #define KMSG_PRIORITY(PRI)                                 \
     '<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
         '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
@@ -255,28 +258,33 @@
     // Pruning configuration.
     PruneList prune_list;
 
+    std::string buffer_type = GetProperty("logd.buffer_type", "chatty");
+
     // Partial (required for chatty) or full logging statistics.
     bool enable_full_log_statistics = __android_logger_property_get_bool(
             "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
                                        BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
-    LogStatistics log_statistics(enable_full_log_statistics);
+    LogStatistics log_statistics(enable_full_log_statistics, buffer_type == "serialized");
 
-    // Serves the purpose of managing the last logs times read on a
-    // socket connection, and as a reader lock on a range of log
-    // entries.
+    // Serves the purpose of managing the last logs times read on a socket connection, and as a
+    // reader lock on a range of log entries.
     LogReaderList reader_list;
 
     // LogBuffer is the object which is responsible for holding all log entries.
-    LogBuffer* logBuf;
-    if (true) {
-        logBuf = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+    LogBuffer* log_buffer = nullptr;
+    if (buffer_type == "chatty") {
+        log_buffer = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+    } else if (buffer_type == "serialized") {
+        log_buffer = new SerializedLogBuffer(&reader_list, &log_tags, &log_statistics);
+    } else if (buffer_type == "simple") {
+        log_buffer = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
     } else {
-        logBuf = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
+        LOG(FATAL) << "buffer_type must be one of 'chatty', 'serialized', or 'simple'";
     }
 
     // LogReader listens on /dev/socket/logdr. When a client
     // connects, log entries in the LogBuffer are written to the client.
-    LogReader* reader = new LogReader(logBuf, &reader_list);
+    LogReader* reader = new LogReader(log_buffer, &reader_list);
     if (reader->startListener()) {
         return EXIT_FAILURE;
     }
@@ -284,14 +292,14 @@
     // LogListener listens on /dev/socket/logdw for client
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
-    LogListener* swl = new LogListener(logBuf);
+    LogListener* swl = new LogListener(log_buffer);
     if (!swl->StartListener()) {
         return EXIT_FAILURE;
     }
 
     // Command listener listens on /dev/socket/logd for incoming logd
     // administrative commands.
-    CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
+    CommandListener* cl = new CommandListener(log_buffer, &log_tags, &prune_list, &log_statistics);
     if (cl->startListener()) {
         return EXIT_FAILURE;
     }
@@ -304,12 +312,12 @@
         int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
                                ? fdDmesg
                                : -1;
-        al = new LogAudit(logBuf, dmesg_fd, &log_statistics);
+        al = new LogAudit(log_buffer, dmesg_fd, &log_statistics);
     }
 
     LogKlog* kl = nullptr;
     if (klogd) {
-        kl = new LogKlog(logBuf, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
+        kl = new LogKlog(log_buffer, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
     }
 
     readDmesg(al, kl);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index a9d0ed0..ac8e847 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -120,6 +120,17 @@
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
 
+
+# For /vendor_dlkm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /vendor_dlkm partition. Those symlinks are for
+# devices without /vendor_dlkm partition. For devices with /vendor_dlkm
+# partition, mount vendor_dlkm.img under /vendor_dlkm will hide those symlinks.
+# Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed
+# via /vendor/lib/modules directly.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc
+
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else
diff --git a/toolbox/OWNERS b/toolbox/OWNERS
index 682a067..7529cb9 100644
--- a/toolbox/OWNERS
+++ b/toolbox/OWNERS
@@ -1 +1 @@
-enh@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
index af97eba..5de1efa 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.c
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -591,7 +591,7 @@
         return EXIT_SUCCESS;
     }
 
-    open_flags = O_RDWR;
+    open_flags = O_RDWR | O_SYNC;
     if (init) {
         open_flags |= O_CREAT | O_TRUNC;
     }