Merge "adb: delete hellish hodgepodge."
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 1f59d64..dfcc52d 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -94,7 +94,7 @@
     WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
 
-bool reboot_service_impl(unique_fd fd, const std::string& arg) {
+void reboot_service(unique_fd fd, const std::string& arg) {
     std::string reboot_arg = arg;
     bool auto_reboot = false;
 
@@ -108,7 +108,7 @@
     if (reboot_arg == "sideload") {
         if (getuid() != 0) {
             WriteFdExactly(fd.get(), "'adb root' is required for 'adb reboot sideload'.\n");
-            return false;
+            return;
         }
 
         const std::vector<std::string> options = {auto_reboot ? "--sideload_auto_reboot"
@@ -116,7 +116,7 @@
         std::string err;
         if (!write_bootloader_message(options, &err)) {
             D("Failed to set bootloader message: %s", err.c_str());
-            return false;
+            return;
         }
 
         reboot_arg = "recovery";
@@ -128,16 +128,9 @@
     std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
     if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
         WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
-        return false;
-    }
-
-    return true;
-}
-
-void reboot_service(unique_fd fd, const std::string& arg) {
-    if (!reboot_service_impl(std::move(fd), arg)) {
         return;
     }
+
     // Don't return early. Give the reboot command time to take effect
     // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
     while (true) {
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index d6eee6b..eb429b9 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -48,10 +48,20 @@
         PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
         return false;
     }
-    if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
+
+    int alignment_offset;
+    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
         return false;
     }
+    int logical_block_size;
+    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+        return false;
+    }
+
+    device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
+    device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
     return true;
 #else
     (void)block_device;
@@ -178,6 +188,7 @@
 
     device_info_.alignment = geometry_.alignment;
     device_info_.alignment_offset = geometry_.alignment_offset;
+    device_info_.logical_block_size = geometry_.logical_block_size;
     return true;
 }
 
@@ -201,6 +212,10 @@
         LERROR << "Block device size must be a multiple of 512.";
         return false;
     }
+    if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Logical block size must be a multiple of 512.";
+        return false;
+    }
     if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
         LERROR << "Alignment offset is not sector-aligned.";
         return false;
@@ -244,6 +259,18 @@
         return false;
     }
 
+    // Finally, the size of the allocatable space must be a multiple of the
+    // logical block size. If we have no more free space after this
+    // computation, then we abort. Note that the last sector is inclusive,
+    // so we have to account for that.
+    uint64_t num_free_sectors = last_sector - first_sector + 1;
+    uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    if (num_free_sectors < sectors_per_block) {
+        LERROR << "Not enough space to allocate any partition tables.";
+        return false;
+    }
+    last_sector = first_sector + (num_free_sectors / sectors_per_block) * sectors_per_block - 1;
+
     geometry_.first_logical_sector = first_sector;
     geometry_.last_logical_sector = last_sector;
     geometry_.metadata_max_size = metadata_max_size;
@@ -251,6 +278,7 @@
     geometry_.alignment = device_info_.alignment;
     geometry_.alignment_offset = device_info_.alignment_offset;
     geometry_.block_device_size = device_info_.size;
+    geometry_.logical_block_size = device_info.logical_block_size;
     return true;
 }
 
@@ -297,87 +325,93 @@
         uint64_t end;
 
         Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+        uint64_t length() const { return end - start; }
         bool operator<(const Interval& other) const { return start < other.start; }
     };
-    std::vector<Interval> intervals;
 
-    // Collect all extents in the partition table.
+    // Collect all extents in the partition table, then sort them by starting
+    // sector.
+    std::vector<Interval> extents;
     for (const auto& partition : partitions_) {
         for (const auto& extent : partition->extents()) {
             LinearExtent* linear = extent->AsLinearExtent();
             if (!linear) {
                 continue;
             }
-            intervals.emplace_back(linear->physical_sector(),
-                                   linear->physical_sector() + extent->num_sectors());
+            extents.emplace_back(linear->physical_sector(),
+                                 linear->physical_sector() + extent->num_sectors());
         }
     }
+    std::sort(extents.begin(), extents.end());
 
-    // Sort extents by starting sector.
-    std::sort(intervals.begin(), intervals.end());
+    // Convert the extent list into a list of gaps between the extents; i.e.,
+    // the list of ranges that are free on the disk.
+    std::vector<Interval> free_regions;
+    for (size_t i = 1; i < extents.size(); i++) {
+        const Interval& previous = extents[i - 1];
+        const Interval& current = extents[i];
+
+        uint64_t aligned = AlignSector(previous.end);
+        if (aligned >= current.start) {
+            // There is no gap between these two extents, try the next one.
+            // Note that we check with >= instead of >, since alignment may
+            // bump the ending sector past the beginning of the next extent.
+            continue;
+        }
+
+        // The new interval represents the free space starting at the end of
+        // the previous interval, and ending at the start of the next interval.
+        free_regions.emplace_back(aligned, current.start);
+    }
+
+    // Add a final interval representing the remainder of the free space.
+    uint64_t last_free_extent_start =
+            extents.empty() ? geometry_.first_logical_sector : extents.back().end;
+    last_free_extent_start = AlignSector(last_free_extent_start);
+    if (last_free_extent_start <= geometry_.last_logical_sector) {
+        free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1);
+    }
+
+    const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    CHECK(sectors_needed % sectors_per_block == 0);
 
     // Find gaps that we can use for new extents. Note we store new extents in a
     // temporary vector, and only commit them if we are guaranteed enough free
     // space.
     std::vector<std::unique_ptr<LinearExtent>> new_extents;
-    for (size_t i = 1; i < intervals.size(); i++) {
-        const Interval& previous = intervals[i - 1];
-        const Interval& current = intervals[i];
+    for (auto& region : free_regions) {
+        if (region.length() % sectors_per_block != 0) {
+            // This should never happen, because it would imply that we
+            // once allocated an extent that was not a multiple of the
+            // block size. That extent would be rejected by DM_TABLE_LOAD.
+            LERROR << "Region " << region.start << ".." << region.end
+                   << " is not a multiple of the block size, " << sectors_per_block;
 
-        if (previous.end >= current.start) {
-            // There is no gap between these two extents, try the next one. Note that
-            // extents may never overlap, but just for safety, we ignore them if they
-            // do.
-            DCHECK(previous.end == current.start);
-            continue;
+            // If for some reason the final region is mis-sized we still want
+            // to be able to grow partitions. So just to be safe, round the
+            // region down to the nearest block.
+            region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block;
+            if (!region.length()) {
+                continue;
+            }
         }
 
-        uint64_t aligned = AlignSector(previous.end);
-        if (aligned >= current.start) {
-            // After alignment, this extent is not usable.
-            continue;
-        }
+        uint64_t sectors = std::min(sectors_needed, region.length());
+        CHECK(sectors % sectors_per_block == 0);
 
-        // This gap is enough to hold the remainder of the space requested, so we
-        // can allocate what we need and return.
-        if (current.start - aligned >= sectors_needed) {
-            auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
-            sectors_needed -= extent->num_sectors();
-            new_extents.push_back(std::move(extent));
+        auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+        new_extents.push_back(std::move(extent));
+        sectors_needed -= sectors;
+        if (!sectors_needed) {
             break;
         }
-
-        // This gap is not big enough to fit the remainder of the space requested,
-        // so consume the whole thing and keep looking for more.
-        auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
-        sectors_needed -= extent->num_sectors();
-        new_extents.push_back(std::move(extent));
     }
-
-    // If we still have more to allocate, take it from the remaining free space
-    // in the allocatable region.
     if (sectors_needed) {
-        uint64_t first_sector;
-        if (intervals.empty()) {
-            first_sector = geometry_.first_logical_sector;
-        } else {
-            first_sector = intervals.back().end;
-        }
-        DCHECK(first_sector <= geometry_.last_logical_sector);
-
-        // Note: After alignment, |first_sector| may be > the last usable sector.
-        first_sector = AlignSector(first_sector);
-
-        // Note: the last usable sector is inclusive.
-        if (first_sector > geometry_.last_logical_sector ||
-            geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
-            LERROR << "Not enough free space to expand partition: " << partition->name();
-            return false;
-        }
-        auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
-        new_extents.push_back(std::move(extent));
+        LERROR << "Not enough free space to expand partition: " << partition->name();
+        return false;
     }
 
+    // Everything succeeded, so commit the new extents.
     for (auto& extent : new_extents) {
         partition->AddExtent(std::move(extent));
     }
@@ -445,6 +479,12 @@
 void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
     device_info_.size = device_info.size;
 
+    // Note that if the logical block size changes, we're probably in trouble:
+    // we could have already built extents that will only work on the previous
+    // size.
+    DCHECK(partitions_.empty() ||
+           device_info_.logical_block_size == device_info.logical_block_size);
+
     // The kernel does not guarantee these values are present, so we only
     // replace existing values if the new values are non-zero.
     if (device_info.alignment) {
@@ -457,7 +497,7 @@
 
 bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
     // Align the space needed up to the nearest sector.
-    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
+    uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size);
 
     if (aligned_size > partition->size()) {
         return GrowPartition(partition, aligned_size);
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 4334d51..f1a91c4 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -92,11 +92,11 @@
     Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 10000), true);
-    EXPECT_EQ(system->size(), 10240);
+    EXPECT_EQ(system->size(), 12288);
     EXPECT_EQ(system->extents().size(), 1);
 
-    builder->ResizePartition(system, 9000);
-    EXPECT_EQ(system->size(), 9216);
+    builder->ResizePartition(system, 7000);
+    EXPECT_EQ(system->size(), 8192);
     EXPECT_EQ(system->extents().size(), 1);
 }
 
@@ -120,13 +120,13 @@
 
 TEST(liblp, InternalAlignment) {
     // Test the metadata fitting within alignment.
-    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
+    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
 
     // Test a large alignment offset thrown in.
     device_info.alignment_offset = 753664;
@@ -135,7 +135,7 @@
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
 
     // Alignment offset without alignment doesn't mean anything.
     device_info.alignment = 0;
@@ -150,7 +150,7 @@
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 78);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1973);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -163,7 +163,7 @@
 }
 
 TEST(liblp, InternalPartitionAlignment) {
-    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
+    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
 
     Partition* a = builder->AddPartition("a", TEST_GUID, 0);
@@ -381,7 +381,7 @@
     static const size_t kMetadataSize = 64 * 1024;
 
     // No space to store metadata + geometry.
-    BlockDeviceInfo device_info(kDiskSize, 0, 0);
+    BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
@@ -390,8 +390,8 @@
     builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
-    // Space for metadata + geometry + one free sector.
-    device_info.size += LP_SECTOR_SIZE;
+    // Space for metadata + geometry + one free block.
+    device_info.size += device_info.logical_block_size;
     builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_NE(builder, nullptr);
 
@@ -424,19 +424,21 @@
     ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
     ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
     ASSERT_LE(device_info.alignment_offset, INT_MAX);
+    ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
 
     // Having an alignment offset > alignment doesn't really make sense.
     ASSERT_LT(device_info.alignment_offset, device_info.alignment);
 }
 
 TEST(liblp, UpdateBlockDeviceInfo) {
-    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
+    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
     EXPECT_EQ(builder->block_device_info().size, device_info.size);
     EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
     EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+    EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size);
 
     device_info.alignment = 0;
     device_info.alignment_offset = 2048;
@@ -450,3 +452,27 @@
     EXPECT_EQ(builder->block_device_info().alignment, 8192);
     EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
 }
+
+TEST(liblp, InvalidBlockSize) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    EXPECT_EQ(builder, nullptr);
+}
+
+TEST(liblp, AlignedExtentSize) {
+    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
+    ASSERT_NE(partition, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(partition, 512));
+    EXPECT_EQ(partition->size(), 4096);
+}
+
+TEST(liblp, AlignedFreeSpace) {
+    // Only one sector free - at least one block is required.
+    BlockDeviceInfo device_info(10240, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
+    ASSERT_EQ(builder, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 0f96e3a..e83b922 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -32,11 +32,16 @@
 
 // By default, partitions are aligned on a 1MiB boundary.
 static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+static const uint32_t kDefaultBlockSize = 4096;
 
 struct BlockDeviceInfo {
-    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
-    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
-        : size(size), alignment(alignment), alignment_offset(alignment_offset) {}
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
+                    uint32_t logical_block_size)
+        : size(size),
+          alignment(alignment),
+          alignment_offset(alignment_offset),
+          logical_block_size(logical_block_size) {}
     // Size of the block device, in bytes.
     uint64_t size;
     // Optimal target alignment, in bytes. Partition extents will be aligned to
@@ -46,6 +51,8 @@
     // |alignment_offset| on the target device is correctly aligned on its
     // parent device. This value must be 0 or a multiple of 512.
     uint32_t alignment_offset;
+    // Block size, for aligning extent sizes and partition sizes.
+    uint32_t logical_block_size;
 };
 
 // Abstraction around dm-targets that can be encoded into logical partition tables.
@@ -88,6 +95,8 @@
 };
 
 class Partition final {
+    friend class MetadataBuilder;
+
   public:
     Partition(const std::string& name, const std::string& guid, uint32_t attributes);
 
@@ -97,10 +106,6 @@
     // Remove all extents from this partition.
     void RemoveExtents();
 
-    // Remove and/or shrink extents until the partition is the requested size.
-    // See MetadataBuilder::ShrinkPartition for more information.
-    void ShrinkTo(uint64_t requested_size);
-
     const std::string& name() const { return name_; }
     uint32_t attributes() const { return attributes_; }
     const std::string& guid() const { return guid_; }
@@ -108,6 +113,8 @@
     uint64_t size() const { return size_; }
 
   private:
+    void ShrinkTo(uint64_t aligned_size);
+
     std::string name_;
     std::string guid_;
     std::vector<std::unique_ptr<Extent>> extents_;
@@ -144,7 +151,7 @@
     // size. This is a convenience method for tests.
     static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
                                                 uint32_t metadata_slot_count) {
-        BlockDeviceInfo device_info(blockdev_size, 0, 0);
+        BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
         return New(device_info, metadata_max_size, metadata_slot_count);
     }
 
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index e1323e1..52c80f7 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -136,6 +136,12 @@
      * can be used to verify the geometry against a target device.
      */
     uint64_t block_device_size;
+
+    /* 76: Logical block size of the super partition block device. This is the
+     * minimal alignment for partition and extent sizes, and it must be a
+     * multiple of LP_SECTOR_SIZE.
+     */
+    uint32_t logical_block_size;
 } __attribute__((packed)) LpMetadataGeometry;
 
 /* The logical partition metadata has a number of tables; they are described
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 092dbf1..7bf42ae 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -40,7 +40,8 @@
                                    80000,
                                    0,
                                    0,
-                                   1024 * 1024};
+                                   1024 * 1024,
+                                   4096};
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
     EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
diff --git a/init/devices.cpp b/init/devices.cpp
index ed4a739..ba08180 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -372,7 +372,7 @@
     }
 }
 
-void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+void DeviceHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
         FixupSysPermissions(uevent.path, uevent.subsystem);
     }
@@ -418,6 +418,10 @@
     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
 }
 
+void DeviceHandler::ColdbootDone() {
+    skip_restorecon_ = true;
+}
+
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
                              std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
diff --git a/init/devices.h b/init/devices.h
index 0be660f..9d39eaa 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -29,6 +29,7 @@
 #include <selinux/label.h>
 
 #include "uevent.h"
+#include "uevent_handler.h"
 
 namespace android {
 namespace init {
@@ -105,7 +106,7 @@
     std::string dir_name_ = "/dev";
 };
 
-class DeviceHandler {
+class DeviceHandler : public UeventHandler {
   public:
     friend class DeviceHandlerTester;
 
@@ -113,11 +114,12 @@
     DeviceHandler(std::vector<Permissions> dev_permissions,
                   std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
                   std::set<std::string> boot_devices, bool skip_restorecon);
+    virtual ~DeviceHandler() = default;
 
-    void HandleDeviceEvent(const Uevent& uevent);
+    void HandleUevent(const Uevent& uevent) override;
+    void ColdbootDone() override;
 
     std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
-    void set_skip_restorecon(bool value) { skip_restorecon_ = value; }
 
   private:
     bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 28bda34..740e82c 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -35,8 +35,6 @@
 namespace android {
 namespace init {
 
-std::vector<std::string> firmware_directories;
-
 static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
                          int loading_fd, int data_fd) {
     // Start transfer.
@@ -58,7 +56,10 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-static void ProcessFirmwareEvent(const Uevent& uevent) {
+FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories)
+    : firmware_directories_(std::move(firmware_directories)) {}
+
+void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) {
     int booting = IsBooting();
 
     LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
@@ -80,7 +81,7 @@
     }
 
 try_loading_again:
-    for (const auto& firmware_directory : firmware_directories) {
+    for (const auto& firmware_directory : firmware_directories_) {
         std::string file = firmware_directory + uevent.firmware;
         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
         struct stat sb;
@@ -104,7 +105,7 @@
     write(loading_fd, "-1", 2);
 }
 
-void HandleFirmwareEvent(const Uevent& uevent) {
+void FirmwareHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.subsystem != "firmware" || uevent.action != "add") return;
 
     // Loading the firmware in a child means we can do that in parallel...
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 6081511..3996096 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -21,13 +21,23 @@
 #include <vector>
 
 #include "uevent.h"
+#include "uevent_handler.h"
 
 namespace android {
 namespace init {
 
-extern std::vector<std::string> firmware_directories;
+class FirmwareHandler : public UeventHandler {
+  public:
+    explicit FirmwareHandler(std::vector<std::string> firmware_directories);
+    virtual ~FirmwareHandler() = default;
 
-void HandleFirmwareEvent(const Uevent& uevent);
+    void HandleUevent(const Uevent& uevent) override;
+
+  private:
+    void ProcessFirmwareEvent(const Uevent& uevent);
+
+    std::vector<std::string> firmware_directories_;
+};
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 43075b2..41e8fff 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -206,7 +206,7 @@
         bool found = false;
         auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
             if (uevent.path == dm_path) {
-                device_handler_->HandleDeviceEvent(uevent);
+                device_handler_->HandleUevent(uevent);
                 found = true;
                 return ListenerAction::kStop;
             }
@@ -273,7 +273,7 @@
             lp_metadata_partition_ = links[0];
         }
         required_devices_partition_names_.erase(iter);
-        device_handler_->HandleDeviceEvent(uevent);
+        device_handler_->HandleUevent(uevent);
         if (required_devices_partition_names_.empty()) {
             return ListenerAction::kStop;
         } else {
@@ -310,7 +310,7 @@
     auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
         if (uevent.device_name == device_name) {
             LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
-            device_handler_->HandleDeviceEvent(uevent);
+            device_handler_->HandleUevent(uevent);
             found = true;
             return ListenerAction::kStop;
         }
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index 1734a7e..1e0db57 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -139,7 +139,7 @@
     return Insmod(dependencies[0], args);
 }
 
-void ModaliasHandler::HandleModaliasEvent(const Uevent& uevent) {
+void ModaliasHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.modalias.empty()) return;
 
     for (const auto& [alias, module] : module_aliases_) {
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
index e79da32..3247c86 100644
--- a/init/modalias_handler.h
+++ b/init/modalias_handler.h
@@ -16,22 +16,23 @@
 
 #pragma once
 
-#include "result.h"
-#include "uevent.h"
-
 #include <string>
 #include <unordered_map>
 #include <vector>
 
+#include "result.h"
+#include "uevent.h"
+#include "uevent_handler.h"
+
 namespace android {
 namespace init {
 
-class ModaliasHandler {
+class ModaliasHandler : public UeventHandler {
   public:
     ModaliasHandler();
-    ~ModaliasHandler(){};
+    virtual ~ModaliasHandler() = default;
 
-    void HandleModaliasEvent(const Uevent& uevent);
+    void HandleUevent(const Uevent& uevent) override;
 
   private:
     Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
diff --git a/init/uevent_handler.h b/init/uevent_handler.h
new file mode 100644
index 0000000..75d1990
--- /dev/null
+++ b/init/uevent_handler.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 "uevent.h"
+
+namespace android {
+namespace init {
+
+class UeventHandler {
+  public:
+    virtual ~UeventHandler() = default;
+
+    virtual void HandleUevent(const Uevent& uevent) = 0;
+
+    virtual void ColdbootDone() {}
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index e9d829b..95be6af 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -38,6 +38,7 @@
 #include "firmware_handler.h"
 #include "modalias_handler.h"
 #include "selinux.h"
+#include "uevent_handler.h"
 #include "uevent_listener.h"
 #include "ueventd_parser.h"
 #include "util.h"
@@ -107,11 +108,10 @@
 
 class ColdBoot {
   public:
-    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler,
-             ModaliasHandler& modalias_handler)
+    ColdBoot(UeventListener& uevent_listener,
+             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers)
         : uevent_listener_(uevent_listener),
-          device_handler_(device_handler),
-          modalias_handler_(modalias_handler),
+          uevent_handlers_(uevent_handlers),
           num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
 
     void Run();
@@ -124,8 +124,7 @@
     void WaitForSubProcesses();
 
     UeventListener& uevent_listener_;
-    DeviceHandler& device_handler_;
-    ModaliasHandler& modalias_handler_;
+    std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
 
     unsigned int num_handler_subprocesses_;
     std::vector<Uevent> uevent_queue_;
@@ -136,16 +135,16 @@
 void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
     for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
         auto& uevent = uevent_queue_[i];
-        device_handler_.HandleDeviceEvent(uevent);
-        modalias_handler_.HandleModaliasEvent(uevent);
+
+        for (auto& uevent_handler : uevent_handlers_) {
+            uevent_handler->HandleUevent(uevent);
+        }
     }
     _exit(EXIT_SUCCESS);
 }
 
 void ColdBoot::RegenerateUevents() {
     uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
-        HandleFirmwareEvent(uevent);
-
         uevent_queue_.emplace_back(std::move(uevent));
         return ListenerAction::kContinue;
     });
@@ -168,7 +167,6 @@
 
 void ColdBoot::DoRestoreCon() {
     selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    device_handler_.set_skip_restorecon(false);
 }
 
 void ColdBoot::WaitForSubProcesses() {
@@ -234,8 +232,7 @@
     SelinuxSetupKernelLogging();
     SelabelInitialize();
 
-    DeviceHandler device_handler;
-    ModaliasHandler modalias_handler;
+    std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
     UeventListener uevent_listener;
 
     {
@@ -248,19 +245,27 @@
                 ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
                              "/ueventd." + hardware + ".rc"});
 
-        device_handler = DeviceHandler{std::move(ueventd_configuration.dev_permissions),
-                                       std::move(ueventd_configuration.sysfs_permissions),
-                                       std::move(ueventd_configuration.subsystems),
-                                       fs_mgr_get_boot_devices(), true};
+        uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
+                std::move(ueventd_configuration.dev_permissions),
+                std::move(ueventd_configuration.sysfs_permissions),
+                std::move(ueventd_configuration.subsystems), fs_mgr_get_boot_devices(), true));
+        uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
+                std::move(ueventd_configuration.firmware_directories)));
 
-        firmware_directories = ueventd_configuration.firmware_directories;
+        if (ueventd_configuration.enable_modalias_handling) {
+            uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
+        }
     }
 
     if (access(COLDBOOT_DONE, F_OK) != 0) {
-        ColdBoot cold_boot(uevent_listener, device_handler, modalias_handler);
+        ColdBoot cold_boot(uevent_listener, uevent_handlers);
         cold_boot.Run();
     }
 
+    for (auto& uevent_handler : uevent_handlers) {
+        uevent_handler->ColdbootDone();
+    }
+
     // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
     signal(SIGCHLD, SIG_IGN);
     // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
@@ -268,10 +273,10 @@
     while (waitpid(-1, nullptr, WNOHANG) > 0) {
     }
 
-    uevent_listener.Poll([&device_handler, &modalias_handler](const Uevent& uevent) {
-        HandleFirmwareEvent(uevent);
-        modalias_handler.HandleModaliasEvent(uevent);
-        device_handler.HandleDeviceEvent(uevent);
+    uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
+        for (auto& uevent_handler : uevent_handlers) {
+            uevent_handler->HandleUevent(uevent);
+        }
         return ListenerAction::kContinue;
     });
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 54b0d16..677938e 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -84,6 +84,23 @@
     return Success();
 }
 
+Result<Success> ParseModaliasHandlingLine(std::vector<std::string>&& args,
+                                          bool* enable_modalias_handling) {
+    if (args.size() != 2) {
+        return Error() << "modalias_handling lines take exactly one parameter";
+    }
+
+    if (args[1] == "enabled") {
+        *enable_modalias_handling = true;
+    } else if (args[1] == "disabled") {
+        *enable_modalias_handling = false;
+    } else {
+        return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
+    }
+
+    return Success();
+}
+
 class SubsystemParser : public SectionParser {
   public:
     SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
@@ -182,6 +199,9 @@
     parser.AddSingleLineParser("firmware_directories",
                                std::bind(ParseFirmwareDirectoriesLine, _1,
                                          &ueventd_configuration.firmware_directories));
+    parser.AddSingleLineParser("modalias_handling",
+                               std::bind(ParseModaliasHandlingLine, _1,
+                                         &ueventd_configuration.enable_modalias_handling));
 
     for (const auto& config : configs) {
         parser.ParseConfig(config);
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 343d58b..7d30edf 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -30,6 +30,7 @@
     std::vector<SysfsPermissions> sysfs_permissions;
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
+    bool enable_modalias_handling = false;
 };
 
 UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);