Merge "init: skip RejectsCriticalAndOneshotService for devices launched before R"
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/fs_mgr/Android.bp b/fs_mgr/Android.bp
index cd64599..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: [
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/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/libnetutils/Android.bp b/libnetutils/Android.bp
index 268496f..65371fa 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -23,6 +23,21 @@
     export_include_dirs: ["include"],
 }
 
+cc_library_static {
+    name: "libipchecksum",
+
+    srcs: [
+        "checksum.c",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
 cc_binary {
     name: "dhcpdbg",
 
diff --git a/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/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 19c22c8..8cc780a 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -341,6 +341,37 @@
 }
 
 //-------------------------------------------------------------------------
+// Fuzzers
+//-------------------------------------------------------------------------
+cc_defaults {
+    name: "libunwindstack_fuzz_defaults",
+    host_supported: true,
+    defaults: ["libunwindstack_flags"],
+    cflags: [
+        "-Wno-exit-time-destructors",
+        "-g",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "liblzma",
+        "libunwindstack",
+        "libdexfile_support",
+    ],
+}
+
+cc_fuzz {
+    name: "libunwindstack_fuzz_unwinder",
+    defaults: ["libunwindstack_fuzz_defaults"],
+    srcs: [
+        "tests/MemoryFake.cpp",
+        "tests/ElfFake.cpp",
+        "tests/fuzz/UnwinderComponentCreator.cpp",
+        "tests/fuzz/UnwinderFuzz.cpp",
+    ],
+}
+
+//-------------------------------------------------------------------------
 // Tools
 //-------------------------------------------------------------------------
 cc_defaults {
@@ -458,3 +489,4 @@
         "tests/GenGnuDebugdata.cpp",
     ],
 }
+
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
new file mode 100644
index 0000000..94f5a73
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnwinderComponentCreator.h"
+
+std::unique_ptr<Regs> GetRegisters(ArchEnum arch) {
+  switch (arch) {
+    case unwindstack::ARCH_ARM: {
+      std::unique_ptr<unwindstack::RegsArm> regs = std::make_unique<unwindstack::RegsArm>();
+      return regs;
+    }
+    case unwindstack::ARCH_ARM64: {
+      std::unique_ptr<unwindstack::RegsArm64> regs = std::make_unique<unwindstack::RegsArm64>();
+      return regs;
+    }
+    case unwindstack::ARCH_X86: {
+      std::unique_ptr<unwindstack::RegsX86> regs = std::make_unique<unwindstack::RegsX86>();
+      return regs;
+    }
+    case unwindstack::ARCH_X86_64: {
+      std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+      return regs;
+    }
+    case unwindstack::ARCH_MIPS: {
+      std::unique_ptr<unwindstack::RegsMips> regs = std::make_unique<unwindstack::RegsMips>();
+      return regs;
+    }
+    case unwindstack::ARCH_MIPS64: {
+      std::unique_ptr<unwindstack::RegsMips64> regs = std::make_unique<unwindstack::RegsMips64>();
+      return regs;
+    }
+    case unwindstack::ARCH_UNKNOWN:
+    default: {
+      std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+      return regs;
+    }
+  }
+}
+
+ArchEnum GetArch(FuzzedDataProvider* data_provider) {
+  uint8_t arch = data_provider->ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+  return static_cast<ArchEnum>(arch);
+}
+
+void ElfAddMapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+                   const char* name, Elf* elf = nullptr) {
+  std::string str_name(name);
+  maps->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+  if (elf != nullptr) {
+    const auto& map_info = *--maps->end();
+    map_info->elf.reset(elf);
+  }
+}
+
+void ElfPushFakeFunctionData(FuzzedDataProvider* data_provider, ElfInterfaceFake* elf) {
+  uint8_t func_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxFuncCount);
+  for (uint8_t i = 0; i < func_count; i++) {
+    std::string func_name = data_provider->ConsumeRandomLengthString(kMaxFuncNameLen);
+    bool global = data_provider->ConsumeBool();
+    if (global) {
+      elf->FakeSetGlobalVariable(func_name, data_provider->ConsumeIntegral<uint64_t>());
+    } else {
+      ElfInterfaceFake::FakePushFunctionData(FunctionData(func_name, i));
+    }
+  }
+}
+void ElfPushFakeStepData(FuzzedDataProvider* data_provider) {
+  uint8_t step_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxStepCount);
+  for (uint8_t i = 0; i < step_count; i++) {
+    uint64_t pc = data_provider->ConsumeIntegral<uint64_t>();
+    uint64_t sp = data_provider->ConsumeIntegral<uint64_t>();
+    bool finished = i + 1 == step_count;
+    ElfInterfaceFake::FakePushStepData(StepData(pc, sp, finished));
+  }
+}
+
+ElfFake* PopulateElfFake(FuzzedDataProvider* data_provider) {
+  // This will be passed to a smart pointer in ElfAddMapInfo.
+  ElfFake* elf = new ElfFake(new MemoryFake);
+
+  // This will be handled by a smart pointer within Elf.
+  ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+  std::string build_id = data_provider->ConsumeRandomLengthString(kMaxBuildIdLen);
+  interface_fake->FakeSetBuildID(build_id);
+  std::string so_name = data_provider->ConsumeRandomLengthString(kMaxSoNameLen);
+  interface_fake->FakeSetSoname(so_name.c_str());
+
+  elf->FakeSetArch(GetArch(data_provider));
+  elf->FakeSetLoadBias(data_provider->ConsumeIntegral<uint64_t>());
+
+  ElfPushFakeFunctionData(data_provider, interface_fake);
+  ElfPushFakeStepData(data_provider);
+
+  elf->FakeSetInterface(interface_fake);
+  ElfInterfaceFake::FakeClear();
+  return elf;
+}
+
+std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) {
+  std::unique_ptr<Maps> maps = std::make_unique<Maps>();
+  uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount);
+  for (uint8_t i = 0; i < entry_count; i++) {
+    uint64_t start = data_provider->ConsumeIntegral<uint64_t>();
+    uint64_t end = data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX);
+    uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+    std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen);
+    uint8_t flags = PROT_READ | PROT_WRITE;
+
+    bool exec = data_provider->ConsumeBool();
+    if (exec) {
+      flags |= PROT_EXEC;
+    }
+
+    bool shouldAddElf = data_provider->ConsumeBool();
+    if (shouldAddElf) {
+      ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str(),
+                    PopulateElfFake(data_provider));
+    } else {
+      ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str());
+    }
+  }
+  maps->Sort();
+  return maps;
+}
+
+// This code (until PutElfFilesInMemory) is pretty much directly copied from JitDebugTest.cpp
+// There's a few minor modifications, most notably, all methods accept a MemoryFake pointer, and
+// PutElfInMemory inserts JIT data when called.
+void WriteDescriptor32(MemoryFake* memory, uint64_t addr, uint32_t entry) {
+  // Format of the 32 bit JITDescriptor structure:
+  //   uint32_t version
+  memory->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory->SetData32(addr + 4, 0);
+  //   uint32_t relevant_entry
+  memory->SetData32(addr + 8, 0);
+  //   uint32_t first_entry
+  memory->SetData32(addr + 12, entry);
+}
+
+void WriteDescriptor64(MemoryFake* memory, uint64_t addr, uint64_t entry) {
+  // Format of the 64 bit JITDescriptor structure:
+  //   uint32_t version
+  memory->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory->SetData32(addr + 4, 0);
+  //   uint64_t relevant_entry
+  memory->SetData64(addr + 8, 0);
+  //   uint64_t first_entry
+  memory->SetData64(addr + 16, entry);
+}
+
+void WriteEntry32Pack(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+                      uint32_t elf_addr, uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory->SetData32(addr, next);
+  //   uint32_t prev
+  memory->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory->SetData32(addr + 8, elf_addr);
+  //   uint64_t symfile_size
+  memory->SetData64(addr + 12, elf_size);
+}
+
+void WriteEntry32Pad(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+                     uint32_t elf_addr, uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory->SetData32(addr, next);
+  //   uint32_t prev
+  memory->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory->SetData32(addr + 8, elf_addr);
+  //   uint32_t pad
+  memory->SetData32(addr + 12, 0);
+  //   uint64_t symfile_size
+  memory->SetData64(addr + 16, elf_size);
+}
+
+void WriteEntry64(MemoryFake* memory, uint64_t addr, uint64_t prev, uint64_t next,
+                  uint64_t elf_addr, uint64_t elf_size) {
+  // Format of the 64 bit JITCodeEntry structure:
+  //   uint64_t next
+  memory->SetData64(addr, next);
+  //   uint64_t prev
+  memory->SetData64(addr + 8, prev);
+  //   uint64_t symfile_addr
+  memory->SetData64(addr + 16, elf_addr);
+  //   uint64_t symfile_size
+  memory->SetData64(addr + 24, elf_size);
+}
+
+template <typename EhdrType, typename ShdrType>
+void PutElfInMemory(MemoryFake* memory, uint64_t offset, uint8_t class_type, uint8_t machine_type,
+                    uint32_t pc, uint32_t size) {
+  EhdrType ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  uint64_t sh_offset = sizeof(ehdr);
+  memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+  ehdr.e_ident[EI_CLASS] = class_type;
+  ehdr.e_machine = machine_type;
+  ehdr.e_shstrndx = 1;
+  ehdr.e_shoff = sh_offset;
+  ehdr.e_shentsize = sizeof(ShdrType);
+  ehdr.e_shnum = 3;
+  memory->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+  ShdrType shdr;
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NULL;
+  memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x500;
+  shdr.sh_size = 0x100;
+  memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+  memory->SetMemory(offset + 0x500, ".debug_frame");
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0;
+  shdr.sh_addr = 0x600;
+  shdr.sh_offset = 0x600;
+  shdr.sh_size = 0x200;
+  memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+  // Now add a single cie/fde.
+  uint64_t dwarf_offset = offset + 0x600;
+  if (class_type == ELFCLASS32) {
+    // CIE 32 information.
+    memory->SetData32(dwarf_offset, 0xfc);
+    memory->SetData32(dwarf_offset + 0x4, 0xffffffff);
+    memory->SetData8(dwarf_offset + 0x8, 1);
+    memory->SetData8(dwarf_offset + 0x9, '\0');
+    memory->SetData8(dwarf_offset + 0xa, 0x4);
+    memory->SetData8(dwarf_offset + 0xb, 0x4);
+    memory->SetData8(dwarf_offset + 0xc, 0x1);
+
+    // FDE 32 information.
+    memory->SetData32(dwarf_offset + 0x100, 0xfc);
+    memory->SetData32(dwarf_offset + 0x104, 0);
+    memory->SetData32(dwarf_offset + 0x108, pc);
+    memory->SetData32(dwarf_offset + 0x10c, size);
+  } else {
+    // CIE 64 information.
+    memory->SetData32(dwarf_offset, 0xffffffff);
+    memory->SetData64(dwarf_offset + 4, 0xf4);
+    memory->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+    memory->SetData8(dwarf_offset + 0x14, 1);
+    memory->SetData8(dwarf_offset + 0x15, '\0');
+    memory->SetData8(dwarf_offset + 0x16, 0x4);
+    memory->SetData8(dwarf_offset + 0x17, 0x4);
+    memory->SetData8(dwarf_offset + 0x18, 0x1);
+
+    // FDE 64 information.
+    memory->SetData32(dwarf_offset + 0x100, 0xffffffff);
+    memory->SetData64(dwarf_offset + 0x104, 0xf4);
+    memory->SetData64(dwarf_offset + 0x10c, 0);
+    memory->SetData64(dwarf_offset + 0x114, pc);
+    memory->SetData64(dwarf_offset + 0x11c, size);
+  }
+}
+
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider) {
+  uint8_t elf_file_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxJitElfFiles);
+  int entry_offset = 0;
+  int prev_jit_addr = 0;
+  for (uint8_t i = 0; i < elf_file_count; i++) {
+    uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+    // Technically the max valid value is ELFCLASSNUM - 1 (2), but
+    // we want to test values outside of that range.
+    uint8_t class_type = data_provider->ConsumeIntegral<uint8_t>();
+    // Same here, EM_NUM is 253, max valid machine type is 252
+    uint8_t machine_type = data_provider->ConsumeIntegral<uint8_t>();
+    uint32_t pc = data_provider->ConsumeIntegral<uint32_t>();
+    uint32_t size = data_provider->ConsumeIntegral<uint32_t>();
+    bool sixty_four_bit = data_provider->ConsumeBool();
+    bool write_jit = data_provider->ConsumeBool();
+    if (sixty_four_bit) {
+      PutElfInMemory<Elf64_Ehdr, Elf64_Shdr>(memory, offset, class_type, machine_type, pc, size);
+    } else {
+      PutElfInMemory<Elf32_Ehdr, Elf32_Shdr>(memory, offset, class_type, machine_type, pc, size);
+    }
+    if (write_jit) {
+      bool use_pad = data_provider->ConsumeBool();
+      // It is possible this will overwrite part of the ELF.
+      // This provides an interesting test of how malformed ELF
+      // data is handled.
+      uint64_t cur_descriptor_addr = 0x11800 + entry_offset;
+      uint64_t cur_jit_addr = 0x200000 + entry_offset;
+      uint64_t next_jit_addr = cur_jit_addr + size;
+      if (sixty_four_bit) {
+        WriteDescriptor64(memory, 0x11800, cur_jit_addr);
+        WriteEntry64(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+      } else {
+        // Loop back. Again, this may corrupt data,
+        // but that will allow for testing edge cases with
+        // malformed JIT data.
+        if (cur_jit_addr > UINT32_MAX) {
+          entry_offset = 0;
+          cur_jit_addr = 0x200000;
+          cur_descriptor_addr = 0x11800;
+          next_jit_addr = cur_jit_addr + size;
+        }
+        WriteDescriptor32(memory, cur_descriptor_addr, cur_jit_addr);
+        if (use_pad) {
+          WriteEntry32Pad(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+        } else {
+          WriteEntry32Pack(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+        }
+      }
+      entry_offset += size;
+      prev_jit_addr = cur_jit_addr;
+    }
+  }
+}
+
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+                                       uint max_strings) {
+  uint str_count = data_provider->ConsumeIntegralInRange<uint>(0, max_strings);
+  std::vector<std::string> strings;
+  for (uint i = 0; i < str_count; i++) {
+    strings.push_back(data_provider->ConsumeRandomLengthString(max_str_len));
+  }
+  return strings;
+}
+
+std::unique_ptr<DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+                                      std::shared_ptr<Memory> memory, uint max_library_length,
+                                      uint max_libraries) {
+  std::vector<std::string> search_libs =
+      GetStringList(data_provider, max_library_length, max_libraries);
+  if (search_libs.size() <= 0) {
+    return std::make_unique<DexFiles>(memory);
+  }
+
+  return std::make_unique<DexFiles>(memory, search_libs);
+}
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
new file mode 100644
index 0000000..09b3379
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+#define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+
+#include <elf.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+#include "../ElfFake.h"
+#include "../MemoryFake.h"
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using unwindstack::ArchEnum;
+using unwindstack::DexFiles;
+using unwindstack::Elf;
+using unwindstack::ElfFake;
+using unwindstack::ElfInterfaceFake;
+using unwindstack::FunctionData;
+using unwindstack::Maps;
+using unwindstack::Memory;
+using unwindstack::MemoryFake;
+using unwindstack::Regs;
+using unwindstack::StepData;
+
+static constexpr uint8_t kArchCount = 6;
+
+static constexpr uint8_t kMaxSoNameLen = 150;
+
+static constexpr uint8_t kMaxFuncNameLen = 50;
+static constexpr uint8_t kMaxFuncCount = 100;
+
+static constexpr uint8_t kMaxJitElfFiles = 20;
+static constexpr uint8_t kJitElfPadding = 32;
+
+static constexpr uint8_t kMaxStepCount = 100;
+static constexpr uint8_t kMaxMapEntryCount = 50;
+static constexpr uint8_t kMaxBuildIdLen = 100;
+static constexpr uint8_t kMaxMapInfoNameLen = 150;
+
+std::unique_ptr<unwindstack::Regs> GetRegisters(unwindstack::ArchEnum arch);
+std::unique_ptr<unwindstack::Maps> GetMaps(FuzzedDataProvider* data_provider);
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+                                       uint max_strings);
+unwindstack::ArchEnum GetArch(FuzzedDataProvider* data_provider);
+
+void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name,
+                Elf* elf = nullptr);
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider);
+
+std::unique_ptr<unwindstack::DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+                                                   std::shared_ptr<unwindstack::Memory> memory,
+                                                   uint max_libraries, uint max_library_length);
+#endif  // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
new file mode 100644
index 0000000..2f4986a
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <iostream>
+#include <vector>
+
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Unwinder.h>
+
+#include "../MemoryFake.h"
+#include "UnwinderComponentCreator.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+namespace unwindstack {
+
+static constexpr int kMaxUnwindStringLen = 50;
+static constexpr int kMaxUnwindStrings = 50;
+
+void PerformUnwind(FuzzedDataProvider* data_provider, Unwinder* unwinder) {
+  // 0 = don't set any values
+  // 1 = set initial_map_names_to_skip
+  // 2 = set map_suffixes_to_ignore
+  // 3 = set both
+  uint8_t set_values = data_provider->ConsumeIntegral<uint8_t>() % 4;
+  if (set_values == 0) {
+    unwinder->Unwind();
+  } else if (set_values == 1) {
+    // Only setting initial_map_names_to_skip
+    std::vector<std::string> skip_names =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+    unwinder->Unwind(&skip_names, nullptr);
+  } else if (set_values == 2) {
+    // Only setting map_suffixes_to_ignore
+    std::vector<std::string> ignore_suffixes =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+    unwinder->Unwind(nullptr, &ignore_suffixes);
+  } else if (set_values == 3) {
+    // Setting both values
+    std::vector<std::string> skip_names =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+    std::vector<std::string> ignore_suffixes =
+        GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+    unwinder->Unwind(&skip_names, &ignore_suffixes);
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+
+  // We need to construct an unwinder.
+  // Generate the Maps:
+  std::unique_ptr<Maps> maps = GetMaps(&data_provider);
+
+  // Generate the Regs:
+  uint8_t arch_val = data_provider.ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+  ArchEnum arch = static_cast<ArchEnum>(arch_val);
+  std::unique_ptr<Regs> regs = GetRegisters(arch);
+
+  // Generate memory:
+  std::shared_ptr<Memory> memory = std::make_shared<MemoryFake>();
+  PutElfFilesInMemory(reinterpret_cast<MemoryFake*>(memory.get()), &data_provider);
+
+  size_t max_frames = data_provider.ConsumeIntegralInRange<size_t>(0, 5000);
+
+  std::unique_ptr<JitDebug> jit_debug_ptr = std::make_unique<JitDebug>(memory);
+
+  // Create instance
+  Unwinder unwinder(max_frames, maps.get(), regs.get(), memory);
+  unwinder.SetJitDebug(jit_debug_ptr.get(), arch);
+  unwinder.SetResolveNames(data_provider.ConsumeBool());
+  // Call unwind
+  PerformUnwind(&data_provider, &unwinder);
+
+  // Run some additional logic that changes after unwind
+  uint64_t pc = data_provider.ConsumeIntegral<uint64_t>();
+  unwinder.BuildFrameFromPcOnly(pc);
+  unwinder.ConsumeFrames();
+  return 0;
+}
+}  // namespace unwindstack
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 7392806..9bd15c6 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -78,6 +78,9 @@
         "libcutils",
         "liblog",
     ],
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
 
     target: {
         android: {