Merge "Add a libipchecksum that contains the C IP checksum code."
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index e162aaa..f2e722a 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -104,7 +104,9 @@
             SetLineMessage("pulling");
             status_ =
                 br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;
-            if (status_ != 0) {
+            if (status_ == 0) {
+                printf("Bug report copied to %s\n", destination.c_str());
+            } else {
                 fprintf(stderr,
                         "Bug report finished but could not be copied to '%s'.\n"
                         "Try to run 'adb pull %s <directory>'\n"
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 9611212..a0fc9ca 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -249,6 +249,9 @@
             return false;
         }
 
+        // Remove any services with the same instance name, as it may be a stale registration.
+        removeDNSService(regType_.c_str(), serviceName_.c_str());
+
         // Add to the service registry before trying to auto-connect, since socket_spec_connect will
         // check these registries for the ip address when connecting via mdns instance name.
         int adbSecureServiceType = serviceIndex();
@@ -268,13 +271,6 @@
                 return false;
         }
 
-        if (!services->empty()) {
-            // Remove the previous resolved service, if any.
-            services->erase(std::remove_if(services->begin(), services->end(),
-                                           [&](std::unique_ptr<ResolvedService>& service) {
-                                               return (serviceName_ == service->serviceName());
-                                           }));
-        }
         services->push_back(std::unique_ptr<ResolvedService>(this));
 
         if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
@@ -327,6 +323,8 @@
     static bool connectByServiceName(const ServiceRegistry& services,
                                      const std::string& service_name);
 
+    static void removeDNSService(const char* regType, const char* serviceName);
+
   private:
     int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
     std::string addr_format_;
@@ -396,6 +394,37 @@
     return false;
 }
 
+// static
+void ResolvedService::removeDNSService(const char* regType, const char* serviceName) {
+    D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
+    int index = adb_DNSServiceIndexByName(regType);
+    ServiceRegistry* services;
+    switch (index) {
+        case kADBTransportServiceRefIndex:
+            services = sAdbTransportServices;
+            break;
+        case kADBSecurePairingServiceRefIndex:
+            services = sAdbSecurePairingServices;
+            break;
+        case kADBSecureConnectServiceRefIndex:
+            services = sAdbSecureConnectServices;
+            break;
+        default:
+            return;
+    }
+
+    if (services->empty()) {
+        return;
+    }
+
+    std::string sName(serviceName);
+    services->erase(std::remove_if(services->begin(), services->end(),
+                                   [&sName](std::unique_ptr<ResolvedService>& service) {
+                                       return (sName == service->serviceName());
+                                   }),
+                    services->end());
+}
+
 void adb_secure_foreach_pairing_service(const char* service_name,
                                         adb_secure_foreach_service_callback cb) {
     ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
@@ -481,35 +510,6 @@
     std::string regType_;
 };
 
-static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
-    D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
-    int index = adb_DNSServiceIndexByName(regType);
-    ResolvedService::ServiceRegistry* services;
-    switch (index) {
-        case kADBTransportServiceRefIndex:
-            services = ResolvedService::sAdbTransportServices;
-            break;
-        case kADBSecurePairingServiceRefIndex:
-            services = ResolvedService::sAdbSecurePairingServices;
-            break;
-        case kADBSecureConnectServiceRefIndex:
-            services = ResolvedService::sAdbSecureConnectServices;
-            break;
-        default:
-            return;
-    }
-
-    if (services->empty()) {
-        return;
-    }
-
-    std::string sName(serviceName);
-    services->erase(std::remove_if(services->begin(), services->end(),
-                                   [&sName](std::unique_ptr<ResolvedService>& service) {
-                                       return (sName == service->serviceName());
-                                   }));
-}
-
 // Returns the version the device wanted to advertise,
 // or -1 if parsing fails.
 static int parse_version_from_txt_record(uint16_t txtLen, const unsigned char* txtRecord) {
@@ -612,7 +612,7 @@
     } else {
         D("%s: Discover lost serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
           regtype, domain);
-        adb_RemoveDNSService(regtype, serviceName);
+        ResolvedService::removeDNSService(regtype, serviceName);
     }
 }
 
diff --git a/adb/test_adb.py b/adb/test_adb.py
index b9f0d54..a32d875 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -618,21 +618,37 @@
     finally:
         zeroconf_ctx.unregister_service(info)
 
+@contextlib.contextmanager
+def zeroconf_register_services(zeroconf_ctx, infos):
+    """Context manager for multiple zeroconf services
+
+    Registers all services given and unregisters all on cleanup. Returns the ServiceInfo
+    list supplied.
+    """
+
+    try:
+        for info in infos:
+            zeroconf_ctx.register_service(info)
+        yield infos
+    finally:
+        for info in infos:
+            zeroconf_ctx.unregister_service(info)
+
 """Should match the service names listed in adb_mdns.h"""
 class MdnsTest:
     """Tests for adb mdns."""
+    @staticmethod
+    def _mdns_services(port):
+        output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+        return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+    @staticmethod
+    def _devices(port):
+        output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+        return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
 
     class Base(unittest.TestCase):
-        @staticmethod
-        def _mdns_services(port):
-            output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
-            return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
-
-        @staticmethod
-        def _devices(port):
-            output = subprocess.check_output(["adb", "-P", str(port), "devices"])
-            return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
-
         @contextlib.contextmanager
         def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect):
             """Context manager for an ADB connection.
@@ -691,6 +707,50 @@
                         for line in MdnsTest._mdns_services(server_port)))
 
         @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+        def test_mdns_services_register_unregister_multiple(self):
+            """Ensure that `adb mdns services` correctly adds and removes multiple services
+            """
+            from zeroconf import IPVersion, ServiceInfo
+
+            with adb_server() as server_port:
+                output = subprocess.check_output(["adb", "-P", str(server_port),
+                                                  "mdns", "services"]).strip()
+                self.assertTrue(output.startswith(b"List of discovered mdns services"))
+
+                """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
+                """Register/Unregister a service"""
+                with zeroconf_context(IPVersion.V4Only) as zc:
+                    srvs = {
+                        'mdns_name': ["testservice0", "testservice1", "testservice2"],
+                        'mdns_type': "_" + self.service_name + "._tcp.",
+                        'ipaddr': [
+                            socket.inet_aton("192.168.0.1"),
+                            socket.inet_aton("10.0.0.255"),
+                            socket.inet_aton("172.16.1.100")],
+                        'port': [10000, 20000, 65535]}
+                    srv_infos = []
+                    for i in range(len(srvs['mdns_name'])):
+                        srv_infos.append(ServiceInfo(
+                                srvs['mdns_type'] + "local.",
+                                name=srvs['mdns_name'][i] + "." + srvs['mdns_type'] + "local.",
+                                addresses=[srvs['ipaddr'][i]],
+                                port=srvs['port'][i]))
+
+                    """ Register all devices, then unregister"""
+                    with zeroconf_register_services(zc, srv_infos) as infos:
+                        """Give adb some time to register the service"""
+                        time.sleep(1)
+                        for i in range(len(srvs['mdns_name'])):
+                            self.assertTrue(any((srvs['mdns_name'][i] in line and srvs['mdns_type'] in line)
+                                for line in MdnsTest._mdns_services(server_port)))
+
+                    """Give adb some time to unregister the service"""
+                    time.sleep(1)
+                    for i in range(len(srvs['mdns_name'])):
+                        self.assertFalse(any((srvs['mdns_name'][i] in line and srvs['mdns_type'] in line)
+                            for line in MdnsTest._mdns_services(server_port)))
+
+        @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
         def test_mdns_connect(self):
             """Ensure that `adb connect` by mdns instance name works (for non-pairing services)
             """
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 5d6cee4..8979e9a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -436,6 +436,8 @@
     {"reboot,userspace_failed,watchdog_fork", 188},
     {"reboot,userspace_failed,*", 189},
     {"reboot,mount_userdata_failed", 190},
+    {"reboot,forcedsilent", 191},
+    {"reboot,forcednonsilent", 192},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0e9713d..86cf30d 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -992,10 +992,69 @@
            fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
 }
 
+static std::string fb_fix_numeric_var(std::string var) {
+    // Some bootloaders (angler, for example), send spurious leading whitespace.
+    var = android::base::Trim(var);
+    // Some bootloaders (hammerhead, for example) use implicit hex.
+    // This code used to use strtol with base 16.
+    if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
+    return var;
+}
+
+static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+    if (buf->sz < AVB_FOOTER_SIZE) {
+        return;
+    }
+
+    std::string partition_size_str;
+    if (fb->GetVar("partition-size:" + partition, &partition_size_str) != fastboot::SUCCESS) {
+        die("cannot get boot partition size");
+    }
+
+    partition_size_str = fb_fix_numeric_var(partition_size_str);
+    int64_t partition_size;
+    if (!android::base::ParseInt(partition_size_str, &partition_size)) {
+        die("Couldn't parse partition size '%s'.", partition_size_str.c_str());
+    }
+    if (partition_size == buf->sz) {
+        return;
+    }
+    if (partition_size < buf->sz) {
+        die("boot partition is smaller than boot image");
+    }
+
+    std::string data;
+    if (!android::base::ReadFdToString(buf->fd, &data)) {
+        die("Failed reading from boot");
+    }
+
+    uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
+    if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
+        return;
+    }
+
+    int fd = make_temporary_fd("boot rewriting");
+    if (!android::base::WriteStringToFd(data, fd)) {
+        die("Failed writing to modified boot");
+    }
+    lseek(fd, partition_size - AVB_FOOTER_SIZE, SEEK_SET);
+    if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
+        die("Failed copying AVB footer in boot");
+    }
+    close(buf->fd);
+    buf->fd = fd;
+    buf->sz = partition_size;
+    lseek(fd, 0, SEEK_SET);
+}
+
 static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
 {
     sparse_file** s;
 
+    if (partition == "boot" || partition == "boot_a" || partition == "boot_b") {
+        copy_boot_avb_footer(partition, buf);
+    }
+
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
     if (g_disable_verity || g_disable_verification) {
         if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
@@ -1491,15 +1550,6 @@
     fb->RawCommand(command, "");
 }
 
-static std::string fb_fix_numeric_var(std::string var) {
-    // Some bootloaders (angler, for example), send spurious leading whitespace.
-    var = android::base::Trim(var);
-    // Some bootloaders (hammerhead, for example) use implicit hex.
-    // This code used to use strtol with base 16.
-    if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
-    return var;
-}
-
 static unsigned fb_get_flash_block_size(std::string name) {
     std::string sizeString;
     if (fb->GetVar(name, &sizeString) != fastboot::SUCCESS || sizeString.empty()) {
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index f5daf91..ac784b2 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -149,6 +149,14 @@
         darwin: {
             enabled: false,
         },
+        vendor: {
+            cflags: [
+                // Skipping entries in fstab should only be done in a system
+                // process as the config file is in /system_ext.
+                // Remove the op from the vendor variant.
+                "-DNO_SKIP_MOUNT",
+            ],
+        },
     },
     export_include_dirs: ["include_fstab"],
     header_libs: [
@@ -162,10 +170,13 @@
     defaults: ["fs_mgr_defaults"],
     static_libs: [
         "libavb_user",
+        "libutils",
+        "libvold_binder",
     ],
     shared_libs: [
         "libbootloader_message",
         "libbase",
+        "libbinder",
         "libcutils",
         "libcrypto",
         "libext4_utils",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 0c184af..0ae5787 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1044,7 +1044,8 @@
 
 class CheckpointManager {
   public:
-    CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
+    CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false)
+        : needs_checkpoint_(needs_checkpoint), metadata_encrypted_(metadata_encrypted) {}
 
     bool NeedsCheckpoint() {
         if (needs_checkpoint_ != UNKNOWN) {
@@ -1062,7 +1063,7 @@
             return true;
         }
 
-        if (entry->fs_mgr_flags.checkpoint_blk) {
+        if (entry->fs_mgr_flags.checkpoint_blk && !metadata_encrypted_) {
             call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device}, nullptr);
         }
 
@@ -1171,6 +1172,7 @@
 
     enum { UNKNOWN = -1, NO = 0, YES = 1 };
     int needs_checkpoint_;
+    bool metadata_encrypted_;
     std::map<std::string, std::string> device_map_;
 };
 
@@ -1804,11 +1806,11 @@
 // in turn, and stop on 1st success, or no more match.
 static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
                                   const std::string& n_blk_device, const char* tmp_mount_point,
-                                  int needs_checkpoint) {
+                                  int needs_checkpoint, bool metadata_encrypted) {
     int mount_errors = 0;
     int first_mount_errno = 0;
     std::string mount_point;
-    CheckpointManager checkpoint_manager(needs_checkpoint);
+    CheckpointManager checkpoint_manager(needs_checkpoint, metadata_encrypted);
     AvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -1918,12 +1920,13 @@
 }
 
 int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1, false);
 }
 
 int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
-                    bool needs_checkpoint) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
+                    bool needs_checkpoint, bool metadata_encrypted) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint,
+                                  metadata_encrypted);
 }
 
 /*
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f333a85..54102ec 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -696,7 +696,9 @@
         TransformFstabForDsu(fstab, Split(lp_names, ","));
     }
 
+#ifndef NO_SKIP_MOUNT
     SkipMountingPartitions(fstab);
+#endif
     EnableMandatoryFlags(fstab);
 
     return true;
@@ -726,11 +728,14 @@
         return false;
     }
 
+#ifndef NO_SKIP_MOUNT
     SkipMountingPartitions(fstab);
+#endif
 
     return true;
 }
 
+#ifndef NO_SKIP_MOUNT
 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
@@ -762,6 +767,7 @@
 
     return true;
 }
+#endif
 
 // Loads the fstab file and combines with fstab entries passed in from device tree.
 bool ReadDefaultFstab(Fstab* fstab) {
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 052efa7..b8b074e 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -24,6 +24,7 @@
 #include <unistd.h>
 
 #include <string>
+#include <thread>
 #include <utility>
 #include <vector>
 
@@ -31,6 +32,8 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
+#include <android/os/IVold.h>
+#include <binder/IServiceManager.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <fec/io.h>
@@ -103,8 +106,23 @@
     ::exit(0);  // SUCCESS
 }
 
+static android::sp<android::os::IVold> GetVold() {
+    while (true) {
+        if (auto sm = android::defaultServiceManager()) {
+            if (auto binder = sm->getService(android::String16("vold"))) {
+                if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
+                    return vold;
+                }
+            }
+        }
+        std::this_thread::sleep_for(2s);
+    }
+}
+
 }  // namespace
 
+using namespace std::chrono_literals;
+
 enum RemountStatus {
     REMOUNT_SUCCESS = 0,
     NOT_USERDEBUG,
@@ -117,7 +135,9 @@
     BAD_OVERLAY,
     NO_MOUNTS,
     REMOUNT_FAILED,
-    MUST_REBOOT
+    MUST_REBOOT,
+    BINDER_ERROR,
+    CHECKPOINTING
 };
 
 static int do_remount(int argc, char* argv[]) {
@@ -194,6 +214,22 @@
         return NO_FSTAB;
     }
 
+    if (android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
+        !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
+        // Virtual A/B devices can use /data as backing storage; make sure we're
+        // not checkpointing.
+        auto vold = GetVold();
+        bool checkpointing = false;
+        if (!vold->isCheckpointing(&checkpointing).isOk()) {
+            LOG(ERROR) << "Could not determine checkpointing status.";
+            return BINDER_ERROR;
+        }
+        if (checkpointing) {
+            LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
+            return CHECKPOINTING;
+        }
+    }
+
     // Generate the list of supported overlayfs mount points.
     auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 86090c1..2a67b8c 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -69,7 +69,7 @@
 int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
                     char* tmp_mount_point);
 int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
-                    char* tmp_mount_point, bool need_cp);
+                    char* tmp_mount_point, bool need_cp, bool metadata_encrypted);
 int fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,
                         const std::string& mount_point = "");
 int fs_mgr_do_tmpfs_mount(const char *n_name);
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 c37d70e..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) {}
 
@@ -205,11 +236,18 @@
         }
     }
 
-    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false) &&
-        !always_keep_source_slot) {
-        if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
-                                              target_slot_number)) {
-            return nullptr;
+    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+        if (always_keep_source_slot) {
+            // always_keep_source_slot implies the target build does not support snapshots.
+            // Clear unsupported attributes.
+            SetMetadataHeaderV0(metadata.get());
+        } else {
+            // !always_keep_source_slot implies the target build supports snapshots. Do snapshot
+            // updates.
+            if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
+                                                  target_slot_number)) {
+                return nullptr;
+            }
         }
     }
 
@@ -511,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();
@@ -520,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();
@@ -1263,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 89a47b1..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 {
@@ -208,8 +225,10 @@
     // metadata may not have the target slot's devices listed yet, in which
     // case, it is automatically upgraded to include all available block
     // devices.
-    // If |always_keep_source_slot| is set, on a Virtual A/B device, source slot
-    // partitions are kept. This is useful when applying a downgrade package.
+    // If |always_keep_source_slot| is set, on a Virtual A/B device
+    // - source slot partitions are kept.
+    // - UPDATED flag is cleared.
+    // This is useful when applying a downgrade package.
     static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
                                                          const std::string& source_partition,
                                                          uint32_t source_slot_number,
@@ -241,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.
@@ -265,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/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 48c5c83..d8e171b 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdint.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -29,6 +30,7 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <ext4_utils/ext4_utils.h>
 #include <openssl/sha.h>
 
@@ -285,5 +287,42 @@
     return true;
 }
 
+inline std::string ToHexString(uint64_t value) {
+    return android::base::StringPrintf("0x%" PRIx64, value);
+}
+
+void SetMetadataHeaderV0(LpMetadata* metadata) {
+    if (metadata->header.minor_version <= LP_METADATA_MINOR_VERSION_MIN) {
+        return;
+    }
+    LINFO << "Forcefully setting metadata header version " << LP_METADATA_MAJOR_VERSION << "."
+          << metadata->header.minor_version << " to " << LP_METADATA_MAJOR_VERSION << "."
+          << LP_METADATA_MINOR_VERSION_MIN;
+    metadata->header.minor_version = LP_METADATA_MINOR_VERSION_MIN;
+    metadata->header.header_size = sizeof(LpMetadataHeaderV1_0);
+
+    // Retrofit Virtual A/B devices should have version 10.1, so flags shouldn't be set.
+    // Warn if this is the case, but zero it out anyways.
+    if (metadata->header.flags) {
+        LWARN << "Zeroing unexpected flags: " << ToHexString(metadata->header.flags);
+    }
+
+    // Zero out all fields beyond LpMetadataHeaderV0.
+    static_assert(sizeof(metadata->header) > sizeof(LpMetadataHeaderV1_0));
+    memset(reinterpret_cast<uint8_t*>(&metadata->header) + sizeof(LpMetadataHeaderV1_0), 0,
+           sizeof(metadata->header) - sizeof(LpMetadataHeaderV1_0));
+
+    // Clear partition attributes unknown to V0.
+    // On retrofit Virtual A/B devices, UPDATED flag may be set, so only log info here.
+    for (auto& partition : metadata->partitions) {
+        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK_V0) {
+            LINFO << "Clearing " << GetPartitionName(partition)
+                  << " partition attribute: " << ToHexString(partition.attributes);
+        }
+
+        partition.attributes &= LP_PARTITION_ATTRIBUTE_MASK_V0;
+    }
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index c4fe3ed..aa3a6a0 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -103,6 +103,10 @@
 bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
                                       uint32_t target_slot_number);
 
+// Forcefully set metadata header version to 1.0, clearing any incompatible flags and attributes
+// so that when downgrading to a build with liblp V0, the device still boots.
+void SetMetadataHeaderV0(LpMetadata* metadata);
+
 }  // namespace fs_mgr
 }  // namespace android
 
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/Android.bp b/init/Android.bp
index edf9099..3f2cd07 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -130,6 +130,7 @@
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
         "libsnapshot_init",
+        "libxml2",
         "lib_apex_manifest_proto_lite",
         "update_metadata-protos",
     ],
@@ -164,6 +165,9 @@
         "selinux_policy_version",
     ],
     srcs: init_common_sources + init_device_sources,
+    generated_sources: [
+        "apex-info-list",
+    ],
     whole_static_libs: [
         "libcap",
         "com.android.sysprop.apex",
@@ -178,6 +182,12 @@
     target: {
         recovery: {
             cflags: ["-DRECOVERY"],
+            exclude_static_libs: [
+                "libxml2",
+            ],
+            exclude_generated_sources: [
+                "apex-info-list",
+            ],
             exclude_shared_libs: [
                 "libbinder",
                 "libutils",
@@ -212,6 +222,9 @@
     target: {
         recovery: {
             cflags: ["-DRECOVERY"],
+            exclude_static_libs: [
+                "libxml2",
+            ],
             exclude_shared_libs: [
                 "libbinder",
                 "libutils",
diff --git a/init/devices.cpp b/init/devices.cpp
index 9fbec64..53ca875 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -193,7 +193,8 @@
     while (directory != "/" && directory != ".") {
         std::string subsystem_link_path;
         if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
-            subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
+            (subsystem_link_path == sysfs_mount_point_ + "/bus/platform" ||
+             subsystem_link_path == sysfs_mount_point_ + "/bus/amba")) {
             // We need to remove the mount point that we added above before returning.
             directory.erase(0, sysfs_mount_point_.size());
             *platform_device_path = directory;
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/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/mount_namespace.cpp b/init/mount_namespace.cpp
index b9d5d67..f8359bc 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -27,10 +27,19 @@
 #include <android-base/properties.h>
 #include <android-base/result.h>
 #include <android-base/unique_fd.h>
-#include <apex_manifest.pb.h>
 
 #include "util.h"
 
+#ifndef RECOVERY
+#define ACTIVATE_FLATTENED_APEX 1
+#endif
+
+#ifdef ACTIVATE_FLATTENED_APEX
+#include <apex_manifest.pb.h>
+#include <com_android_apex.h>
+#include <selinux/android.h>
+#endif  // ACTIVATE_FLATTENED_APEX
+
 namespace android {
 namespace init {
 namespace {
@@ -106,6 +115,8 @@
     return updatable;
 }
 
+#ifdef ACTIVATE_FLATTENED_APEX
+
 static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
     if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
         return ErrnoError() << "Could not create mount point " << mount_path;
@@ -116,7 +127,7 @@
     return {};
 }
 
-static Result<std::string> GetApexName(const std::string& apex_dir) {
+static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
     const std::string manifest_path = apex_dir + "/apex_manifest.pb";
     std::string content;
     if (!android::base::ReadFileToString(manifest_path, &content)) {
@@ -126,11 +137,12 @@
     if (!manifest.ParseFromString(content)) {
         return Error() << "Can't parse manifest file: " << manifest_path;
     }
-    return manifest.name();
+    return manifest;
 }
 
+template <typename Fn>
 static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
-                                                const std::string& to_dir) {
+                                                const std::string& to_dir, Fn on_activate) {
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
     if (!dir) {
         return {};
@@ -140,15 +152,16 @@
         if (entry->d_name[0] == '.') continue;
         if (entry->d_type == DT_DIR) {
             const std::string apex_path = from_dir + "/" + entry->d_name;
-            const auto apex_name = GetApexName(apex_path);
-            if (!apex_name.ok()) {
-                LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
+            const auto apex_manifest = GetApexManifest(apex_path);
+            if (!apex_manifest.ok()) {
+                LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
                 continue;
             }
-            const std::string mount_path = to_dir + "/" + (*apex_name);
+            const std::string mount_path = to_dir + "/" + apex_manifest->name();
             if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
                 return result;
             }
+            on_activate(apex_path, *apex_manifest);
         }
     }
     return {};
@@ -167,15 +180,37 @@
             "/vendor/apex",
     };
 
+    std::vector<com::android::apex::ApexInfo> apex_infos;
+    auto on_activate = [&](const std::string& apex_path,
+                           const apex::proto::ApexManifest& apex_manifest) {
+        apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
+                                apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true);
+    };
+
     for (const auto& dir : kBuiltinDirsForApexes) {
-        if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop); !result.ok()) {
+        if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
             LOG(ERROR) << result.error();
             return false;
         }
     }
+
+    std::ostringstream oss;
+    com::android::apex::ApexInfoList apex_info_list(apex_infos);
+    com::android::apex::write(oss, apex_info_list);
+    const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
+    if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
+        PLOG(ERROR) << "Failed to write " << kApexInfoList;
+        return false;
+    }
+    if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
+        PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
+    }
+
     return true;
 }
 
+#endif  // ACTIVATE_FLATTENED_APEX
+
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -269,9 +304,9 @@
         default_ns_fd.reset(OpenMountNamespace());
         default_ns_id = GetMountNamespaceId();
     }
-
+#ifdef ACTIVATE_FLATTENED_APEX
     success &= ActivateFlattenedApexesIfPossible();
-
+#endif
     LOG(INFO) << "SetupMountNamespaces done";
     return success;
 }
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 612854d..b593b62 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;
@@ -886,24 +888,60 @@
         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_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/libbacktrace/Android.bp b/libbacktrace/Android.bp
index f75e8df..c7969f2 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -96,6 +96,8 @@
 cc_library {
     name: "libbacktrace",
     vendor_available: false,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     recovery_available: true,
     apex_available: [
         "//apex_available:platform",
@@ -120,6 +122,9 @@
         recovery: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
         },
+        native_bridge: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+        },
     },
 }
 
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 238431f..a5c5edd 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -78,18 +78,21 @@
 static bool descriptive_output = false;
 
 /*
- *  gnome-terminal color tags
- *    See http://misc.flogisoft.com/bash/tip_colors_and_formatting
- *    for ideas on how to set the forground color of the text for xterm.
- *    The color manipulation character stream is defined as:
- *      ESC [ 3 8 ; 5 ; <color#> m
+ * 8-bit color tags. See ECMA-48 Set Graphics Rendition in
+ * [console_codes(4)](https://man7.org/linux/man-pages/man4/console_codes.4.html).
+ *
+ * The text manipulation character stream is defined as:
+ *   ESC [ <parameter #> m
+ *
+ * We use "set <color> foreground" escape sequences instead of
+ * "256/24-bit foreground color". This allows colors to render
+ * according to user preferences in terminal emulator settings
  */
-#define ANDROID_COLOR_BLUE 75
-#define ANDROID_COLOR_DEFAULT 231
-#define ANDROID_COLOR_GREEN 40
-#define ANDROID_COLOR_ORANGE 166
-#define ANDROID_COLOR_RED 196
-#define ANDROID_COLOR_YELLOW 226
+#define ANDROID_COLOR_BLUE 34
+#define ANDROID_COLOR_DEFAULT 39
+#define ANDROID_COLOR_GREEN 32
+#define ANDROID_COLOR_RED 31
+#define ANDROID_COLOR_YELLOW 33
 
 static FilterInfo* filterinfo_new(const char* tag, android_LogPriority pri) {
   FilterInfo* p_ret;
@@ -165,7 +168,7 @@
     case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
     case ANDROID_LOG_DEBUG:   return ANDROID_COLOR_BLUE;
     case ANDROID_LOG_INFO:    return ANDROID_COLOR_GREEN;
-    case ANDROID_LOG_WARN:    return ANDROID_COLOR_ORANGE;
+    case ANDROID_LOG_WARN:    return ANDROID_COLOR_YELLOW;
     case ANDROID_LOG_ERROR:   return ANDROID_COLOR_RED;
     case ANDROID_LOG_FATAL:   return ANDROID_COLOR_RED;
     case ANDROID_LOG_SILENT:  return ANDROID_COLOR_DEFAULT;
@@ -1499,7 +1502,7 @@
    */
   if (p_format->colored_output) {
     prefixLen =
-        snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority));
+        snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[%dm", colorFromPri(entry->priority));
     prefixLen = MIN(prefixLen, sizeof(prefixBuf));
 
     const char suffixContents[] = "\x1B[0m";
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 4806b08..baee4f9 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -37,7 +37,6 @@
     void ResetModuleCount() { module_count_ = 0; }
     int GetModuleCount() { return module_count_; }
     void EnableBlocklist(bool enable);
-    void EnableVerbose(bool enable);
 
   private:
     std::string MakeCanonical(const std::string& module_path);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 5a6ae8b..bbdd317 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -343,14 +343,6 @@
     blocklist_enabled = enable;
 }
 
-void Modprobe::EnableVerbose(bool enable) {
-    if (enable) {
-        android::base::SetMinimumLogSeverity(android::base::VERBOSE);
-    } else {
-        android::base::SetMinimumLogSeverity(android::base::INFO);
-    }
-}
-
 std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
     auto it = module_deps_.find(module);
     if (it == module_deps_.end()) {
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 4af4589..a638fca 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -44,6 +44,11 @@
 #define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
 #define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
 
+void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
+    controller_ = controller;
+    file_name_ = file_name;
+}
+
 bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
     std::string subgroup;
     if (!controller()->GetTaskGroup(tid, &subgroup)) {
@@ -380,15 +385,16 @@
         std::string controller_name = attr[i]["Controller"].asString();
         std::string file_attr = attr[i]["File"].asString();
 
-        if (attributes_.find(name) == attributes_.end()) {
-            auto controller = cg_map.FindController(controller_name);
-            if (controller.HasValue()) {
+        auto controller = cg_map.FindController(controller_name);
+        if (controller.HasValue()) {
+            auto iter = attributes_.find(name);
+            if (iter == attributes_.end()) {
                 attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
             } else {
-                LOG(WARNING) << "Controller " << controller_name << " is not found";
+                iter->second->Reset(controller, file_attr);
             }
         } else {
-            LOG(WARNING) << "Attribute " << name << " is already defined";
+            LOG(WARNING) << "Controller " << controller_name << " is not found";
         }
     }
 
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 28bc00c..2983a09 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -33,6 +33,7 @@
 
     const CgroupController* controller() const { return &controller_; }
     const std::string& file_name() const { return file_name_; }
+    void Reset(const CgroupController& controller, const std::string& file_name);
 
     bool GetPathForTask(int tid, std::string* path) const;
 
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15b0d89..ae45742 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,8 @@
     name: "libprocinfo",
     defaults: ["libprocinfo_defaults"],
     vendor_available: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     recovery_available: true,
     vndk: {
         enabled: true,
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 3c44534..8cc780a 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -124,6 +124,8 @@
     name: "libunwindstack",
     vendor_available: true,
     recovery_available: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -145,6 +147,11 @@
             exclude_srcs: ["DexFile.cpp"],
             exclude_shared_libs: ["libdexfile_support"],
         },
+        native_bridge: {
+            cflags: ["-UDEXFILE_SUPPORT"],
+            exclude_srcs: ["DexFile.cpp"],
+            exclude_shared_libs: ["libdexfile_support"],
+        },
     },
 
     apex_available: [
@@ -334,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 {
@@ -451,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 ea39d34..9bd15c6 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -78,6 +78,9 @@
         "libcutils",
         "liblog",
     ],
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
 
     target: {
         android: {
@@ -169,6 +172,8 @@
 cc_library {
     name: "libutilscallstack",
     defaults: ["libutils_defaults"],
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
 
     srcs: [
         "CallStack.cpp",
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index cf98dad..d15fa2b 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -402,8 +402,8 @@
         "  time       — Display the date, invocation time, priority/tag, and PID of the\n"
         "             process issuing the message.\n"
         "\nAdverb modifiers can be used in combination:\n"
-        "  color       — Display in highlighted color to match priority. i.e. \x1B[38;5;231mVERBOSE\n"
-        "                \x1B[38;5;75mDEBUG \x1B[38;5;40mINFO \x1B[38;5;166mWARNING \x1B[38;5;196mERROR FATAL\x1B[0m\n"
+        "  color       — Display in highlighted color to match priority. i.e. \x1B[39mVERBOSE\n"
+        "                \x1B[34mDEBUG \x1B[32mINFO \x1B[33mWARNING \x1B[31mERROR FATAL\x1B[0m\n"
         "  descriptive — events logs only, descriptions from event-log-tags database.\n"
         "  epoch       — Display time as seconds since Jan 1 1970.\n"
         "  monotonic   — Display time as cpu seconds since last boot.\n"
diff --git a/logd/CompressionEngine.cpp b/logd/CompressionEngine.cpp
index f9c5979..da2628c 100644
--- a/logd/CompressionEngine.cpp
+++ b/logd/CompressionEngine.cpp
@@ -27,7 +27,7 @@
     return *engine;
 }
 
-bool ZlibCompressionEngine::Compress(std::span<uint8_t> in, std::vector<uint8_t>& out) {
+bool ZlibCompressionEngine::Compress(SerializedData& in, size_t data_length, SerializedData& out) {
     z_stream strm;
     strm.zalloc = Z_NULL;
     strm.zfree = Z_NULL;
@@ -37,34 +37,34 @@
         LOG(FATAL) << "deflateInit() failed";
     }
 
-    CHECK_LE(in.size(), static_cast<int64_t>(std::numeric_limits<uint32_t>::max()));
-    uint32_t out_size = deflateBound(&strm, in.size());
+    CHECK_LE(data_length, in.size());
+    CHECK_LE(in.size(), std::numeric_limits<uint32_t>::max());
+    uint32_t deflate_bound = deflateBound(&strm, in.size());
 
-    out.resize(out_size);
-    strm.avail_in = in.size();
-    strm.next_in = const_cast<uint8_t*>(in.data());
-    strm.avail_out = out_size;
+    out.Resize(deflate_bound);
+
+    strm.avail_in = data_length;
+    strm.next_in = in.data();
+    strm.avail_out = out.size();
     strm.next_out = out.data();
     ret = deflate(&strm, Z_FINISH);
     CHECK_EQ(ret, Z_STREAM_END);
 
-    uint32_t compressed_data_size = strm.total_out;
+    uint32_t compressed_size = strm.total_out;
     deflateEnd(&strm);
-    out.resize(compressed_data_size);
+
+    out.Resize(compressed_size);
 
     return true;
 }
 
-bool ZlibCompressionEngine::Decompress(const std::vector<uint8_t>& in, std::vector<uint8_t>& out,
-                                       size_t out_size) {
-    out.resize(out_size);
-
+bool ZlibCompressionEngine::Decompress(SerializedData& in, SerializedData& out) {
     z_stream strm;
     strm.zalloc = Z_NULL;
     strm.zfree = Z_NULL;
     strm.opaque = Z_NULL;
     strm.avail_in = in.size();
-    strm.next_in = const_cast<uint8_t*>(in.data());
+    strm.next_in = in.data();
     strm.avail_out = out.size();
     strm.next_out = out.data();
 
@@ -79,22 +79,22 @@
     return true;
 }
 
-bool ZstdCompressionEngine::Compress(std::span<uint8_t> in, std::vector<uint8_t>& out) {
-    size_t out_size = ZSTD_compressBound(in.size());
-    out.resize(out_size);
+bool ZstdCompressionEngine::Compress(SerializedData& in, size_t data_length, SerializedData& out) {
+    CHECK_LE(data_length, in.size());
 
-    out_size = ZSTD_compress(out.data(), out_size, in.data(), in.size(), 1);
+    size_t compress_bound = ZSTD_compressBound(data_length);
+    out.Resize(compress_bound);
+
+    size_t out_size = ZSTD_compress(out.data(), out.size(), in.data(), data_length, 1);
     if (ZSTD_isError(out_size)) {
         LOG(FATAL) << "ZSTD_compress failed: " << ZSTD_getErrorName(out_size);
     }
-    out.resize(out_size);
+    out.Resize(out_size);
 
     return true;
 }
 
-bool ZstdCompressionEngine::Decompress(const std::vector<uint8_t>& in, std::vector<uint8_t>& out,
-                                       size_t out_size) {
-    out.resize(out_size);
+bool ZstdCompressionEngine::Decompress(SerializedData& in, SerializedData& out) {
     size_t result = ZSTD_decompress(out.data(), out.size(), in.data(), in.size());
     if (ZSTD_isError(result)) {
         LOG(FATAL) << "ZSTD_decompress failed: " << ZSTD_getErrorName(result);
diff --git a/logd/CompressionEngine.h b/logd/CompressionEngine.h
index d760cea..0f760ed 100644
--- a/logd/CompressionEngine.h
+++ b/logd/CompressionEngine.h
@@ -16,8 +16,9 @@
 
 #pragma once
 
-#include <span>
-#include <vector>
+#include <memory>
+
+#include "SerializedData.h"
 
 class CompressionEngine {
   public:
@@ -25,23 +26,20 @@
 
     virtual ~CompressionEngine(){};
 
-    virtual bool Compress(std::span<uint8_t> in, std::vector<uint8_t>& out) = 0;
-    // Decompress the contents of `in` into `out`.  `out_size` must be set to the decompressed size
-    // of the contents.
-    virtual bool Decompress(const std::vector<uint8_t>& in, std::vector<uint8_t>& out,
-                            size_t out_size) = 0;
+    virtual bool Compress(SerializedData& in, size_t data_length, SerializedData& out) = 0;
+    // Decompress the contents of `in` into `out`.  `out.size()` must be set to the decompressed
+    // size of the contents.
+    virtual bool Decompress(SerializedData& in, SerializedData& out) = 0;
 };
 
 class ZlibCompressionEngine : public CompressionEngine {
   public:
-    bool Compress(std::span<uint8_t> in, std::vector<uint8_t>& out) override;
-    bool Decompress(const std::vector<uint8_t>& in, std::vector<uint8_t>& out,
-                    size_t out_size) override;
+    bool Compress(SerializedData& in, size_t data_length, SerializedData& out) override;
+    bool Decompress(SerializedData& in, SerializedData& out) override;
 };
 
 class ZstdCompressionEngine : public CompressionEngine {
   public:
-    bool Compress(std::span<uint8_t> in, std::vector<uint8_t>& out) override;
-    bool Decompress(const std::vector<uint8_t>& in, std::vector<uint8_t>& out,
-                    size_t out_size) override;
+    bool Compress(SerializedData& in, size_t data_length, SerializedData& out) override;
+    bool Decompress(SerializedData& in, SerializedData& out) override;
 };
\ No newline at end of file
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 8e18f9d..1b7107f 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -276,7 +276,9 @@
             cp++;
         }
     } else if (warn) {
+#ifdef __ANDROID__
         LOG(ERROR) << "Cannot read " << filename;
+#endif
     }
 }
 
diff --git a/logd/SerializedData.h b/logd/SerializedData.h
new file mode 100644
index 0000000..d3d1e18
--- /dev/null
+++ b/logd/SerializedData.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <algorithm>
+#include <memory>
+
+// This class is used instead of std::string or std::vector because their clear(), erase(), etc
+// functions don't actually deallocate.  shrink_to_fit() does deallocate but is not guaranteed to
+// work and swapping with an empty string/vector is clunky.
+class SerializedData {
+  public:
+    SerializedData() {}
+    SerializedData(size_t size) : data_(new uint8_t[size]), size_(size) {}
+
+    void Resize(size_t new_size) {
+        if (size_ == 0) {
+            data_.reset(new uint8_t[new_size]);
+            size_ = new_size;
+        } else if (new_size == 0) {
+            data_.reset();
+            size_ = 0;
+        } else if (new_size != size_) {
+            std::unique_ptr<uint8_t[]> new_data(new uint8_t[new_size]);
+            size_t copy_size = std::min(size_, new_size);
+            memcpy(new_data.get(), data_.get(), copy_size);
+            data_.swap(new_data);
+            size_ = new_size;
+        }
+    }
+
+    uint8_t* data() { return data_.get(); }
+    const uint8_t* data() const { return data_.get(); }
+    size_t size() const { return size_; }
+
+  private:
+    std::unique_ptr<uint8_t[]> data_;
+    size_t size_ = 0;
+};
\ No newline at end of file
diff --git a/logd/SerializedFlushToState.cpp b/logd/SerializedFlushToState.cpp
index 6e2e8b0..2633348 100644
--- a/logd/SerializedFlushToState.cpp
+++ b/logd/SerializedFlushToState.cpp
@@ -121,7 +121,7 @@
 
     log_positions_[log_id]->read_offset += entry->total_len();
 
-    AddMinHeapEntry(log_id);
+    logs_needed_from_next_position_[log_id] = true;
 
     return top;
 }
diff --git a/logd/SerializedFlushToState.h b/logd/SerializedFlushToState.h
index 74b3de5..0b20822 100644
--- a/logd/SerializedFlushToState.h
+++ b/logd/SerializedFlushToState.h
@@ -61,15 +61,13 @@
         if (logs_ == nullptr) logs_ = logs;
     }
 
-    // Checks to see if any log buffers set in logs_needed_from_next_position_ have new logs and
-    // calls AddMinHeapEntry() if so.
-    void CheckForNewLogs();
+    bool HasUnreadLogs() {
+        CheckForNewLogs();
+        return !min_heap_.empty();
+    }
 
-    bool HasUnreadLogs() { return !min_heap_.empty(); }
-
-    // Pops the next unread log from the min heap.  Add the next log for that log_id to the min heap
-    // if one is available otherwise set logs_needed_from_next_position_ to indicate that we're
-    // waiting for more logs.
+    // Pops the next unread log from the min heap and sets logs_needed_from_next_position_ to
+    // indicate that we're waiting for more logs from the associated log buffer.
     MinHeapElement PopNextUnreadLog();
 
     // If the parent log buffer prunes logs, the reference that this class contains may become
@@ -86,6 +84,10 @@
     // first chunk and then first log entry within that chunk that is greater or equal to start().
     void CreateLogPosition(log_id_t log_id);
 
+    // Checks to see if any log buffers set in logs_needed_from_next_position_ have new logs and
+    // calls AddMinHeapEntry() if so.
+    void CheckForNewLogs();
+
     std::list<SerializedLogChunk>* logs_ = nullptr;
     // An optional structure that contains an iterator to the serialized log buffer and offset into
     // it that this logger should handle next.
diff --git a/logd/SerializedFlushToStateTest.cpp b/logd/SerializedFlushToStateTest.cpp
index a1d21ac..f4515c8 100644
--- a/logd/SerializedFlushToStateTest.cpp
+++ b/logd/SerializedFlushToStateTest.cpp
@@ -89,7 +89,6 @@
             for (uint32_t mask = 0; mask < max_mask; ++mask) {
                 auto state = SerializedFlushToState{sequence, mask};
                 state.InitializeLogs(log_chunks_);
-                state.CheckForNewLogs();
                 TestReading(sequence, mask, state);
             }
         }
@@ -109,7 +108,6 @@
                 state.InitializeLogs(log_chunks_);
                 int loop_count = 0;
                 while (write_logs(loop_count++)) {
-                    state.CheckForNewLogs();
                     TestReading(sequence, mask, state);
                     sequence_numbers_per_buffer_.clear();
                 }
@@ -149,7 +147,7 @@
 
     // Add a chunk with the given messages to the a given log buffer.  Keep track of the sequence
     // numbers for future validation.  Optionally mark the block as having finished writing.
-    void AddChunkWithMessages(int buffer, bool finish_writing,
+    void AddChunkWithMessages(bool finish_writing, int buffer,
                               const std::vector<std::string>& messages) {
         auto chunk = SerializedLogChunk{kChunkSize};
         for (const auto& message : messages) {
@@ -252,3 +250,41 @@
 
     TestAllReadingWithFutureMessages(write_logs);
 }
+
+TEST_F(SerializedFlushToStateTest, no_dangling_references) {
+    AddChunkWithMessages(true, 0, {"1st", "2nd"});
+    AddChunkWithMessages(true, 0, {"3rd", "4th"});
+
+    auto state = SerializedFlushToState{1, kLogMaskAll};
+    state.InitializeLogs(log_chunks_);
+
+    ASSERT_EQ(log_chunks_[0].size(), 2U);
+    auto first_chunk = log_chunks_[0].begin();
+    auto second_chunk = std::next(first_chunk);
+
+    ASSERT_TRUE(state.HasUnreadLogs());
+    auto first_log = state.PopNextUnreadLog();
+    EXPECT_STREQ(first_log.entry->msg(), "1st");
+    EXPECT_EQ(first_chunk->reader_ref_count(), 1U);
+    EXPECT_EQ(second_chunk->reader_ref_count(), 0U);
+
+    ASSERT_TRUE(state.HasUnreadLogs());
+    auto second_log = state.PopNextUnreadLog();
+    EXPECT_STREQ(second_log.entry->msg(), "2nd");
+    EXPECT_EQ(first_chunk->reader_ref_count(), 1U);
+    EXPECT_EQ(second_chunk->reader_ref_count(), 0U);
+
+    ASSERT_TRUE(state.HasUnreadLogs());
+    auto third_log = state.PopNextUnreadLog();
+    EXPECT_STREQ(third_log.entry->msg(), "3rd");
+    EXPECT_EQ(first_chunk->reader_ref_count(), 0U);
+    EXPECT_EQ(second_chunk->reader_ref_count(), 1U);
+
+    ASSERT_TRUE(state.HasUnreadLogs());
+    auto fourth_log = state.PopNextUnreadLog();
+    EXPECT_STREQ(fourth_log.entry->msg(), "4th");
+    EXPECT_EQ(first_chunk->reader_ref_count(), 0U);
+    EXPECT_EQ(second_chunk->reader_ref_count(), 1U);
+
+    EXPECT_FALSE(state.HasUnreadLogs());
+}
\ No newline at end of file
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index 70b800f..0f30794 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -16,8 +16,9 @@
 
 #include "SerializedLogBuffer.h"
 
+#include <sys/prctl.h>
+
 #include <limits>
-#include <thread>
 
 #include <android-base/logging.h>
 #include <android-base/scopeguard.h>
@@ -31,7 +32,11 @@
     Init();
 }
 
-SerializedLogBuffer::~SerializedLogBuffer() {}
+SerializedLogBuffer::~SerializedLogBuffer() {
+    if (deleter_thread_.joinable()) {
+        deleter_thread_.join();
+    }
+}
 
 void SerializedLogBuffer::Init() {
     log_id_for_each(i) {
@@ -119,15 +124,44 @@
     }
 }
 
+void SerializedLogBuffer::StartDeleterThread() {
+    if (deleter_thread_running_) {
+        return;
+    }
+    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.  The
-// lambda for the thread takes ownership of the 'chunks' list and thus when the thread ends and the
-// lambda is deleted, the objects are deleted.
-void SerializedLogBuffer::DeleteLogChunks(std::list<SerializedLogChunk>&& chunks, log_id_t log_id) {
-    auto delete_thread = std::thread{[chunks = std::move(chunks), log_id, this]() mutable {
-        for (auto& chunk : chunks) {
+// 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()) {
@@ -137,8 +171,7 @@
             }
             chunk.DecReaderRefCount(false);
         }
-    }};
-    delete_thread.detach();
+    }
 }
 
 void SerializedLogBuffer::NotifyReadersOfPrune(
@@ -164,13 +197,9 @@
         }
     }
 
+    StartDeleterThread();
+
     auto& log_buffer = logs_[log_id];
-
-    std::list<SerializedLogChunk> chunks_to_prune;
-    auto prune_chunks = android::base::make_scope_guard([&chunks_to_prune, log_id, this] {
-        DeleteLogChunks(std::move(chunks_to_prune), log_id);
-    });
-
     auto it = log_buffer.begin();
     while (it != log_buffer.end()) {
         if (oldest != nullptr && it->highest_sequence_number() >= oldest->start()) {
@@ -193,7 +222,8 @@
             }
         } else {
             size_t buffer_size = it_to_prune->PruneSize();
-            chunks_to_prune.splice(chunks_to_prune.end(), log_buffer, it_to_prune);
+            chunks_to_delete_[log_id].splice(chunks_to_delete_[log_id].end(), log_buffer,
+                                             it_to_prune);
             if (buffer_size >= bytes_to_free) {
                 return true;
             }
@@ -246,7 +276,6 @@
 
     auto& state = reinterpret_cast<SerializedFlushToState&>(abstract_state);
     state.InitializeLogs(logs_);
-    state.CheckForNewLogs();
 
     while (state.HasUnreadLogs()) {
         MinHeapElement top = state.PopNextUnreadLog();
@@ -279,10 +308,6 @@
             return false;
         }
         lock.lock();
-
-        // Since we released the log above, buffers that aren't in our min heap may now have had
-        // logs added, so re-check them.
-        state.CheckForNewLogs();
     }
 
     state.set_start(state.start() + 1);
diff --git a/logd/SerializedLogBuffer.h b/logd/SerializedLogBuffer.h
index 346f51f..421d419 100644
--- a/logd/SerializedLogBuffer.h
+++ b/logd/SerializedLogBuffer.h
@@ -21,6 +21,7 @@
 #include <list>
 #include <mutex>
 #include <queue>
+#include <thread>
 #include <vector>
 
 #include <android-base/thread_annotations.h>
@@ -60,7 +61,9 @@
             REQUIRES_SHARED(lock_);
     void NotifyReadersOfPrune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk)
             REQUIRES(reader_list_->reader_threads_lock());
-    void DeleteLogChunks(std::list<SerializedLogChunk>&& chunks, log_id_t log_id);
+
+    void StartDeleterThread() REQUIRES(lock_);
+    void DeleterThread();
 
     LogReaderList* reader_list_;
     LogTags* tags_;
@@ -70,5 +73,9 @@
     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.cpp b/logd/SerializedLogChunk.cpp
index 2516003..e444856 100644
--- a/logd/SerializedLogChunk.cpp
+++ b/logd/SerializedLogChunk.cpp
@@ -25,14 +25,13 @@
 }
 
 void SerializedLogChunk::Compress() {
-    if (compressed_log_.empty()) {
-        CompressionEngine::GetInstance().Compress({contents_.data(), write_offset_},
-                                                  compressed_log_);
+    if (compressed_log_.size() == 0) {
+        CompressionEngine::GetInstance().Compress(contents_, write_offset_, compressed_log_);
         LOG(INFO) << "Compressed Log, buffer max size: " << contents_.size()
                   << " size used: " << write_offset_
                   << " compressed size: " << compressed_log_.size();
     }
-    contents_.resize(0);
+    contents_.Resize(0);
 }
 
 // TODO: Develop a better reference counting strategy to guard against the case where the writer is
@@ -41,7 +40,8 @@
     if (++reader_ref_count_ != 1 || writer_active_) {
         return;
     }
-    CompressionEngine::GetInstance().Decompress(compressed_log_, contents_, write_offset_);
+    contents_.Resize(write_offset_);
+    CompressionEngine::GetInstance().Decompress(compressed_log_, contents_);
 }
 
 void SerializedLogChunk::DecReaderRefCount(bool compress) {
@@ -90,7 +90,7 @@
     // Clear the old compressed logs and set write_offset_ appropriately for DecReaderRefCount()
     // to compress the new partially cleared log.
     if (new_write_offset != write_offset_) {
-        compressed_log_.clear();
+        compressed_log_.Resize(0);
         write_offset_ = new_write_offset;
     }
 
diff --git a/logd/SerializedLogChunk.h b/logd/SerializedLogChunk.h
index a8ac8cd..65a1e86 100644
--- a/logd/SerializedLogChunk.h
+++ b/logd/SerializedLogChunk.h
@@ -18,14 +18,14 @@
 
 #include <sys/types.h>
 
-#include <vector>
-
 #include "LogWriter.h"
+#include "SerializedData.h"
 #include "SerializedLogEntry.h"
 
 class SerializedLogChunk {
   public:
     explicit SerializedLogChunk(size_t size) : contents_(size) {}
+    SerializedLogChunk(SerializedLogChunk&& other) noexcept = default;
     ~SerializedLogChunk();
 
     void Compress();
@@ -60,13 +60,16 @@
     int write_offset() const { return write_offset_; }
     uint64_t highest_sequence_number() const { return highest_sequence_number_; }
 
+    // Exposed for testing
+    uint32_t reader_ref_count() const { return reader_ref_count_; }
+
   private:
     // The decompressed contents of this log buffer.  Deallocated when the ref_count reaches 0 and
     // writer_active_ is false.
-    std::vector<uint8_t> contents_;
+    SerializedData contents_;
     int write_offset_ = 0;
     uint32_t reader_ref_count_ = 0;
     bool writer_active_ = true;
     uint64_t highest_sequence_number_ = 1;
-    std::vector<uint8_t> compressed_log_;
+    SerializedData compressed_log_;
 };
diff --git a/logd/fuzz/Android.bp b/logd/fuzz/Android.bp
index 9834ff0..d346cd7 100644
--- a/logd/fuzz/Android.bp
+++ b/logd/fuzz/Android.bp
@@ -13,11 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-cc_fuzz {
-    name: "log_buffer_log_fuzzer",
-    srcs: [
-        "log_buffer_log_fuzzer.cpp",
-    ],
+
+cc_defaults {
+    name: "log_fuzzer_defaults",
     static_libs: [
         "libbase",
         "libcutils",
@@ -25,9 +23,28 @@
         "liblog",
         "liblogd",
         "libcutils",
-        "libsysutils",
         "libz",
         "libzstd",
     ],
-    cflags: ["-Werror"],
+    cflags: ["-Wextra"],
+    host_supported: true,
+}
+
+cc_fuzz {
+    name: "log_buffer_log_fuzzer",
+    defaults: ["log_fuzzer_defaults"],
+    srcs: [
+        "log_buffer_log_fuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "serialized_log_buffer_fuzzer",
+    defaults: ["log_fuzzer_defaults"],
+    srcs: [
+        "serialized_log_buffer_fuzzer.cpp",
+    ],
+    corpus: [
+        "corpus/logentry_use_after_compress",
+    ]
 }
diff --git a/logd/fuzz/corpus/logentry_use_after_compress b/logd/fuzz/corpus/logentry_use_after_compress
new file mode 100644
index 0000000..2081b13
--- /dev/null
+++ b/logd/fuzz/corpus/logentry_use_after_compress
Binary files differ
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index a7a1792..1dc996c 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -15,10 +15,13 @@
  */
 #include <string>
 
+#include <android-base/logging.h>
+
 #include "../ChattyLogBuffer.h"
 #include "../LogReaderList.h"
 #include "../LogReaderThread.h"
 #include "../LogStatistics.h"
+#include "../SerializedLogBuffer.h"
 
 // We don't want to waste a lot of entropy on messages
 #define MAX_MSG_LENGTH 5
@@ -27,7 +30,20 @@
 #define MIN_TAG_ID 1000
 #define TAG_MOD 10
 
-namespace android {
+#ifndef __ANDROID__
+unsigned long __android_logger_get_buffer_size(log_id_t) {
+    return 1024 * 1024;
+}
+
+bool __android_logger_valid_buffer_size(unsigned long) {
+    return true;
+}
+#endif
+
+char* android::uidToName(uid_t) {
+    return strdup("fake");
+}
+
 struct LogInput {
   public:
     log_id_t log_id;
@@ -79,9 +95,13 @@
     return 1;
 }
 
-char* uidToName(uid_t) {
-    return strdup("fake");
-}
+class NoopWriter : public LogWriter {
+  public:
+    NoopWriter() : LogWriter(0, true) {}
+    bool Write(const logger_entry&, const char*) override { return true; }
+
+    std::string name() const override { return "noop_writer"; }
+};
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     // We want a random tag length and a random remaining message length
@@ -89,11 +109,18 @@
         return 0;
     }
 
+    android::base::SetMinimumLogSeverity(android::base::ERROR);
+
     LogReaderList reader_list;
     LogTags tags;
     PruneList prune_list;
     LogStatistics stats(true);
-    LogBuffer* log_buffer = new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats);
+    std::unique_ptr<LogBuffer> log_buffer;
+#ifdef FUZZ_SERIALIZED
+    log_buffer.reset(new SerializedLogBuffer(&reader_list, &tags, &stats));
+#else
+    log_buffer.reset(new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats));
+#endif
     size_t data_left = size;
     const uint8_t** pdata = &data;
 
@@ -102,12 +129,30 @@
     log_id_for_each(i) { log_buffer->SetSize(i, 10000); }
 
     while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
-        if (!write_log_messages(pdata, &data_left, log_buffer, &stats)) {
+        if (!write_log_messages(pdata, &data_left, log_buffer.get(), &stats)) {
             return 0;
         }
     }
 
+    // Read out all of the logs.
+    {
+        auto lock = std::unique_lock{reader_list.reader_threads_lock()};
+        std::unique_ptr<LogWriter> test_writer(new NoopWriter());
+        std::unique_ptr<LogReaderThread> log_reader(
+                new LogReaderThread(log_buffer.get(), &reader_list, std::move(test_writer), true, 0,
+                                    kLogMaskAll, 0, {}, 1, {}));
+        reader_list.reader_threads().emplace_back(std::move(log_reader));
+    }
+
+    // Wait until the reader has finished.
+    while (true) {
+        usleep(50);
+        auto lock = std::unique_lock{reader_list.reader_threads_lock()};
+        if (reader_list.reader_threads().size() == 0) {
+            break;
+        }
+    }
+
     log_id_for_each(i) { log_buffer->Clear(i, 0); }
     return 0;
 }
-}  // namespace android
diff --git a/logd/fuzz/serialized_log_buffer_fuzzer.cpp b/logd/fuzz/serialized_log_buffer_fuzzer.cpp
new file mode 100644
index 0000000..d4795b0
--- /dev/null
+++ b/logd/fuzz/serialized_log_buffer_fuzzer.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+#define FUZZ_SERIALIZED
+
+#include "log_buffer_log_fuzzer.cpp"
diff --git a/logd/main.cpp b/logd/main.cpp
index 46b6567..e1ec52b 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -69,49 +69,42 @@
 // has a 'sigstop' feature that sends SIGSTOP to a service immediately before calling exec().  This
 // allows debuggers, etc to be attached to logd at the very beginning, while still having init
 // handle the user, groups, capabilities, files, etc setup.
-static int drop_privs(bool klogd, bool auditd) {
-    sched_param param = {};
-
+static void DropPrivs(bool klogd, bool auditd) {
     if (set_sched_policy(0, SP_BACKGROUND) < 0) {
-        PLOG(ERROR) << "failed to set background scheduling policy";
-        return -1;
+        PLOG(FATAL) << "failed to set background scheduling policy";
     }
 
+    sched_param param = {};
     if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
-        PLOG(ERROR) << "failed to set batch scheduler";
-        return -1;
+        PLOG(FATAL) << "failed to set batch scheduler";
     }
 
     if (!__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
         prctl(PR_SET_DUMPABLE, 0) == -1) {
-        PLOG(ERROR) << "failed to clear PR_SET_DUMPABLE";
-        return -1;
+        PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
     }
 
     std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
     if (cap_clear(caps.get()) < 0) {
-        return -1;
+        PLOG(FATAL) << "cap_clear() failed";
     }
-    std::vector<cap_value_t> cap_value;
     if (klogd) {
-        cap_value.emplace_back(CAP_SYSLOG);
+        cap_value_t cap_syslog = CAP_SYSLOG;
+        if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, &cap_syslog, CAP_SET) < 0 ||
+            cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, &cap_syslog, CAP_SET) < 0) {
+            PLOG(FATAL) << "Failed to set CAP_SYSLOG";
+        }
     }
     if (auditd) {
-        cap_value.emplace_back(CAP_AUDIT_CONTROL);
-    }
-
-    if (cap_set_flag(caps.get(), CAP_PERMITTED, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
-        return -1;
-    }
-    if (cap_set_flag(caps.get(), CAP_EFFECTIVE, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
-        return -1;
+        cap_value_t cap_audit_control = CAP_AUDIT_CONTROL;
+        if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, &cap_audit_control, CAP_SET) < 0 ||
+            cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, &cap_audit_control, CAP_SET) < 0) {
+            PLOG(FATAL) << "Failed to set CAP_AUDIT_CONTROL";
+        }
     }
     if (cap_set_proc(caps.get()) < 0) {
-        PLOG(ERROR) << "failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL";
-        return -1;
+        PLOG(FATAL) << "cap_set_proc() failed";
     }
-
-    return 0;
 }
 
 char* android::uidToName(uid_t u) {
@@ -254,9 +247,7 @@
     }
 
     bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
-    if (drop_privs(klogd, auditd) != 0) {
-        return EXIT_FAILURE;
-    }
+    DropPrivs(klogd, auditd);
 
     // A cache of event log tags
     LogTags log_tags;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 01551e2..fb6f1be 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -445,6 +445,9 @@
     # Load persist properties and override properties (if enabled) from /data.
     trigger load_persist_props_action
 
+    # Should be before netd, but after apex, properties and logging is available.
+    trigger load_bpf_programs
+
     # Now we can start zygote for devices with file based encryption
     trigger zygote-start
 
@@ -517,6 +520,13 @@
 
     mkdir /metadata/apex 0700 root system
     mkdir /metadata/apex/sessions 0700 root system
+    # On some devices we see a weird behaviour in which /metadata/apex doesn't
+    # have a correct label. To workaround this bug, explicitly call restorecon
+    # on /metadata/apex. For most of the boot sequences /metadata/apex will
+    # already have a correct selinux label, meaning that this call will be a
+    # no-op.
+    restorecon_recursive /metadata/apex
+
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 3ffa74e..7df7b71 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -17,11 +17,16 @@
 #include <ctype.h>
 #include <getopt.h>
 #include <stdlib.h>
-#include <iostream>
 
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <modprobe/modprobe.h>
 
+namespace {
+
 enum modprobe_mode {
     AddModulesMode,
     RemoveModulesMode,
@@ -29,47 +34,104 @@
     ShowDependenciesMode,
 };
 
-static void print_usage(void) {
-    std::cerr << "Usage:" << std::endl;
-    std::cerr << std::endl;
-    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl;
-    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl;
-    std::cerr << std::endl;
-    std::cerr << "Options:" << std::endl;
-    std::cerr << "  -b: Apply blocklist to module names too" << std::endl;
-    std::cerr << "  -d: Load modules from DIR, option may be used multiple times" << std::endl;
-    std::cerr << "  -D: Print dependencies for modules only, do not load";
-    std::cerr << "  -h: Print this help" << std::endl;
-    std::cerr << "  -l: List modules matching pattern" << std::endl;
-    std::cerr << "  -r: Remove MODULE (multiple modules may be specified)" << std::endl;
-    std::cerr << "  -q: Quiet" << std::endl;
-    std::cerr << "  -v: Verbose" << std::endl;
-    std::cerr << std::endl;
+void print_usage(void) {
+    LOG(INFO) << "Usage:";
+    LOG(INFO);
+    // -d option is required on Android
+    LOG(INFO) << "  modprobe [options] -d DIR [--all=FILE|MODULE]...";
+    LOG(INFO) << "  modprobe [options] -d DIR MODULE [symbol=value]...";
+    LOG(INFO);
+    LOG(INFO) << "Options:";
+    LOG(INFO) << "  --all=FILE: FILE to acquire module names from";
+    LOG(INFO) << "  -b, --use-blocklist: Apply blocklist to module names too";
+    LOG(INFO) << "  -d, --dirname=DIR: Load modules from DIR, option may be used multiple times";
+    LOG(INFO) << "  -D, --show-depends: Print dependencies for modules only, do not load";
+    LOG(INFO) << "  -h, --help: Print this help";
+    LOG(INFO) << "  -l, --list: List modules matching pattern";
+    LOG(INFO) << "  -r, --remove: Remove MODULE (multiple modules may be specified)";
+    LOG(INFO) << "  -s, --syslog: print to syslog also";
+    LOG(INFO) << "  -q, --quiet: disable messages";
+    LOG(INFO) << "  -v, --verbose: enable more messages, even more with a second -v";
+    LOG(INFO);
 }
 
-#define check_mode()                                                      \
-    if (mode != AddModulesMode) {                                         \
-        std::cerr << "Error, multiple mode flags specified" << std::endl; \
-        print_usage();                                                    \
-        return EXIT_FAILURE;                                              \
+#define check_mode()                                   \
+    if (mode != AddModulesMode) {                      \
+        LOG(ERROR) << "multiple mode flags specified"; \
+        print_usage();                                 \
+        return EXIT_FAILURE;                           \
     }
 
+std::string stripComments(const std::string& str) {
+    for (std::string rv = str;;) {
+        auto comment = rv.find('#');
+        if (comment == std::string::npos) return rv;
+        auto end = rv.find('\n', comment);
+        if (end != std::string::npos) end = end - comment;
+        rv.erase(comment, end);
+    }
+    /* NOTREACHED */
+}
+
+auto syslog = false;
+
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+              const char* file, unsigned int line, const char* message) {
+    android::base::StdioLogger(id, severity, tag, file, line, message);
+    if (syslog && message[0]) {
+        android::base::KernelLogger(id, severity, tag, file, line, message);
+    }
+}
+
+}  // anonymous namespace
+
 extern "C" int modprobe_main(int argc, char** argv) {
+    android::base::InitLogging(argv, MyLogger);
+    android::base::SetMinimumLogSeverity(android::base::INFO);
+
     std::vector<std::string> modules;
     std::string module_parameters;
+    std::string mods;
     std::vector<std::string> mod_dirs;
     modprobe_mode mode = AddModulesMode;
     bool blocklist = false;
-    bool verbose = false;
     int rv = EXIT_SUCCESS;
 
     int opt;
-    while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) {
+    int option_index = 0;
+    // NB: We have non-standard short options -l and -D to make it easier for
+    // OEMs to transition from toybox.
+    // clang-format off
+    static struct option long_options[] = {
+        { "all",                 optional_argument, 0, 'a' },
+        { "use-blocklist",       no_argument,       0, 'b' },
+        { "dirname",             required_argument, 0, 'd' },
+        { "show-depends",        no_argument,       0, 'D' },
+        { "help",                no_argument,       0, 'h' },
+        { "list",                no_argument,       0, 'l' },
+        { "quiet",               no_argument,       0, 'q' },
+        { "remove",              no_argument,       0, 'r' },
+        { "syslog",              no_argument,       0, 's' },
+        { "verbose",             no_argument,       0, 'v' },
+    };
+    // clang-format on
+    while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
         switch (opt) {
             case 'a':
                 // toybox modprobe supported -a to load multiple modules, this
-                // is supported here by default, ignore flag
+                // is supported here by default, ignore flag if no argument.
                 check_mode();
+                if (optarg == NULL) break;
+                if (!android::base::ReadFileToString(optarg, &mods)) {
+                    PLOG(ERROR) << "Failed to open " << optarg;
+                    rv = EXIT_FAILURE;
+                }
+                for (auto mod : android::base::Split(stripComments(mods), "\n")) {
+                    mod = android::base::Trim(mod);
+                    if (mod == "") continue;
+                    if (std::find(modules.begin(), modules.end(), mod) != modules.end()) continue;
+                    modules.emplace_back(mod);
+                }
                 break;
             case 'b':
                 blocklist = true;
@@ -82,24 +144,33 @@
                 mode = ShowDependenciesMode;
                 break;
             case 'h':
+                android::base::SetMinimumLogSeverity(android::base::INFO);
                 print_usage();
-                return EXIT_SUCCESS;
+                return rv;
             case 'l':
                 check_mode();
                 mode = ListModulesMode;
                 break;
             case 'q':
-                verbose = false;
+                android::base::SetMinimumLogSeverity(android::base::WARNING);
                 break;
             case 'r':
                 check_mode();
                 mode = RemoveModulesMode;
                 break;
+            case 's':
+                syslog = true;
+                break;
             case 'v':
-                verbose = true;
+                if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+                    android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+                } else {
+                    android::base::SetMinimumLogSeverity(android::base::DEBUG);
+                }
                 break;
             default:
-                std::cerr << "Unrecognized option: " << opt << std::endl;
+                LOG(ERROR) << "Unrecognized option: " << opt;
+                print_usage();
                 return EXIT_FAILURE;
         }
     }
@@ -118,39 +189,33 @@
         }
     }
 
-    if (verbose) {
-        std::cout << "mode is " << mode << std::endl;
-        std::cout << "verbose is " << verbose << std::endl;
-        std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl;
-        std::cout << "modules is: " << android::base::Join(modules, "") << std::endl;
-        std::cout << "module parameters is: " << android::base::Join(module_parameters, "")
-                  << std::endl;
-    }
+    LOG(DEBUG) << "mode is " << mode;
+    LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
+    LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
+    LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
 
     if (modules.empty()) {
         if (mode == ListModulesMode) {
             // emulate toybox modprobe list with no pattern (list all)
             modules.emplace_back("*");
         } else {
-            std::cerr << "No modules given." << std::endl;
+            LOG(ERROR) << "No modules given.";
             print_usage();
             return EXIT_FAILURE;
         }
     }
     if (mod_dirs.empty()) {
-        std::cerr << "No module configuration directories given." << std::endl;
+        LOG(ERROR) << "No module configuration directories given.";
         print_usage();
         return EXIT_FAILURE;
     }
     if (parameter_count && modules.size() > 1) {
-        std::cerr << "Only one module may be loaded when specifying module parameters."
-                  << std::endl;
+        LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
         print_usage();
         return EXIT_FAILURE;
     }
 
     Modprobe m(mod_dirs);
-    m.EnableVerbose(verbose);
     if (blocklist) {
         m.EnableBlocklist(true);
     }
@@ -159,19 +224,19 @@
         switch (mode) {
             case AddModulesMode:
                 if (!m.LoadWithAliases(module, true, module_parameters)) {
-                    std::cerr << "Failed to load module " << module;
+                    PLOG(ERROR) << "Failed to load module " << module;
                     rv = EXIT_FAILURE;
                 }
                 break;
             case RemoveModulesMode:
                 if (!m.Remove(module)) {
-                    std::cerr << "Failed to remove module " << module;
+                    PLOG(ERROR) << "Failed to remove module " << module;
                     rv = EXIT_FAILURE;
                 }
                 break;
             case ListModulesMode: {
                 std::vector<std::string> list = m.ListModules(module);
-                std::cout << android::base::Join(list, "\n") << std::endl;
+                LOG(INFO) << android::base::Join(list, "\n");
                 break;
             }
             case ShowDependenciesMode: {
@@ -182,17 +247,17 @@
                     rv = EXIT_FAILURE;
                     break;
                 }
-                std::cout << "Dependencies for " << module << ":" << std::endl;
-                std::cout << "Soft pre-dependencies:" << std::endl;
-                std::cout << android::base::Join(pre_deps, "\n") << std::endl;
-                std::cout << "Hard dependencies:" << std::endl;
-                std::cout << android::base::Join(deps, "\n") << std::endl;
-                std::cout << "Soft post-dependencies:" << std::endl;
-                std::cout << android::base::Join(post_deps, "\n") << std::endl;
+                LOG(INFO) << "Dependencies for " << module << ":";
+                LOG(INFO) << "Soft pre-dependencies:";
+                LOG(INFO) << android::base::Join(pre_deps, "\n");
+                LOG(INFO) << "Hard dependencies:";
+                LOG(INFO) << android::base::Join(deps, "\n");
+                LOG(INFO) << "Soft post-dependencies:";
+                LOG(INFO) << android::base::Join(post_deps, "\n");
                 break;
             }
             default:
-                std::cerr << "Bad mode";
+                LOG(ERROR) << "Bad mode";
                 rv = EXIT_FAILURE;
         }
     }