ueventd: add support for driver section in ueventd.rc

Allow ueventd configuration to specify what to do with
devices based on driver. This responds to bind events and
treats them similarly to add events.

The format of the driver stanza is exactly the same as
that of the subsystem stanza.

Bug: 376900376
Test: set up cbc_mbim driver stanza and ensure it properly
  creates and destroys device nodes when a USB device with
  that driver appears and disappears or is bound and unbound

Change-Id: I31f5c91bd074d14075b74fe7beefaa6ac07a7ac9
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index aac4acb..0e84c6f 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -76,17 +76,17 @@
 When `/dev/null` is created, its mode will be set to `0666`, its user to `root` and its group to
 `root`.
 
-The path can be modified using a ueventd.rc script and a `subsystem` section. There are three to set
-for a subsystem: the subsystem name, which device name to use, and which directory to place the
-device in. The section takes the below format of
+The path can be modified using a ueventd.rc script and a `subsystem` and/or `driver` section.
+There are three options to set for a subsystem or driver: the name, which device name to use,
+and which directory to place the device in. The section takes the below format of
 
     subsystem <subsystem_name>
       devname uevent_devname|uevent_devpath
       [dirname <directory>]
 
-`subsystem_name` is used to match uevent `SUBSYSTEM` value
+`subsystem_name` is used to match the uevent `SUBSYSTEM` value.
 
-`devname` takes one of three options
+`devname` takes one of three options:
   1. `uevent_devname` specifies that the name of the node will be the uevent `DEVNAME`
   2. `uevent_devpath` specifies that the name of the node will be basename uevent `DEVPATH`
   3. `sys_name` specifies that the name of the node will be the contents of `/sys/DEVPATH/name`
@@ -99,9 +99,13 @@
     subsystem sound
       devname uevent_devpath
       dirname /dev/snd
-Indicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/<basename uevent
+indicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/<basename uevent
 DEVPATH>`.
 
+The `driver` section has the exact same structure as a `subsystem` section, but
+will instead match the `DRIVER` value in a `bind`/`unbind` uevent. However, the
+`driver` section will be ignored for block devices.
+
 ## /sys
 ----
 Ueventd by default takes no action for `/sys`, however it can be instructed to set permissions for
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 7f83037..deb68e9 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -33,7 +33,8 @@
     auto boot_devices = android::fs_mgr::GetBootDevices();
     device_handler_ = std::make_unique<DeviceHandler>(
             std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
-            std::move(boot_devices), android::fs_mgr::GetBootPartUuid(), false);
+            std::vector<Subsystem>{}, std::move(boot_devices), android::fs_mgr::GetBootPartUuid(),
+            false);
 }
 
 // If boot_part_uuid is specified, use it to set boot_devices
diff --git a/init/devices.cpp b/init/devices.cpp
index fafa58f..aeaa431 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -345,6 +345,26 @@
     return FindSubsystemDevice(path, scsi_device_path, subsystem_paths);
 }
 
+void DeviceHandler::TrackDeviceUevent(const Uevent& uevent) {
+    // No need to track any events if we won't bother handling any bind events
+    // later.
+    if (drivers_.size() == 0) return;
+
+    // Only track add, and not for block devices. We don't track remove because
+    // unbind events may arrive after remove events, so unbind will be the
+    // trigger to untrack those events.
+    if ((uevent.action != "add") || uevent.subsystem == "block" ||
+        (uevent.major < 0 || uevent.minor < 0)) {
+        return;
+    }
+
+    std::string path = sysfs_mount_point_ + uevent.path + "/device";
+    std::string device;
+    if (!Realpath(path, &device)) return;
+
+    tracked_uevents_.emplace_back(uevent, device);
+}
+
 void DeviceHandler::FixupSysPermissions(const std::string& upath,
                                         const std::string& subsystem) const {
     // upaths omit the "/sys" that paths in this list
@@ -664,12 +684,53 @@
     return true;
 }
 
+void DeviceHandler::HandleBindInternal(std::string driver_name, std::string action,
+                                       const Uevent& uevent) {
+    if (uevent.subsystem == "block") {
+        LOG(FATAL) << "Tried to handle bind event for block device";
+    }
+
+    // Get tracked uevents for all devices that have this uevent's path as
+    // their canonical device path. Then handle those again if their driver
+    // is one of the ones we're interested in.
+    const auto driver = std::find(drivers_.cbegin(), drivers_.cend(), driver_name);
+    if (driver == drivers_.cend()) return;
+
+    std::string bind_path = sysfs_mount_point_ + uevent.path;
+    for (const TrackedUevent& tracked : tracked_uevents_) {
+        if (tracked.canonical_device_path != bind_path) continue;
+
+        LOG(VERBOSE) << "Propagating " << uevent.action << " as " << action << " for "
+                     << uevent.path;
+
+        std::string devpath = driver->ParseDevPath(tracked.uevent);
+        mkdir_recursive(Dirname(devpath), 0755);
+        HandleDevice(action, devpath, false, tracked.uevent.major, tracked.uevent.minor,
+                     std::vector<std::string>{});
+    }
+}
+
 void DeviceHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "bind" ||
         uevent.action == "online") {
         FixupSysPermissions(uevent.path, uevent.subsystem);
     }
 
+    if (uevent.action == "bind") {
+        bound_drivers_[uevent.path] = uevent.driver;
+        HandleBindInternal(uevent.driver, "add", uevent);
+        return;
+    } else if (uevent.action == "unbind") {
+        if (bound_drivers_.count(uevent.path) == 0) return;
+        HandleBindInternal(bound_drivers_[uevent.path], "remove", uevent);
+
+        std::string sys_path = sysfs_mount_point_ + uevent.path;
+        std::erase_if(tracked_uevents_, [&sys_path](const TrackedUevent& tracked) {
+            return sys_path == tracked.canonical_device_path;
+        });
+        return;
+    }
+
     // if it's not a /dev device, nothing to do
     if (uevent.major < 0 || uevent.minor < 0) return;
 
@@ -677,6 +738,8 @@
     std::vector<std::string> links;
     bool block = false;
 
+    TrackDeviceUevent(uevent);
+
     if (uevent.subsystem == "block") {
         block = true;
         devpath = "/dev/block/" + Basename(uevent.path);
@@ -725,10 +788,12 @@
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
-                             std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
-                             std::string boot_part_uuid, bool skip_restorecon)
+                             std::vector<Subsystem> drivers, std::vector<Subsystem> subsystems,
+                             std::set<std::string> boot_devices, std::string boot_part_uuid,
+                             bool skip_restorecon)
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
+      drivers_(std::move(drivers)),
       subsystems_(std::move(subsystems)),
       boot_devices_(std::move(boot_devices)),
       boot_part_uuid_(boot_part_uuid),
@@ -744,7 +809,8 @@
 
 DeviceHandler::DeviceHandler()
     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
-                    std::vector<Subsystem>{}, std::set<std::string>{}, "", false) {}
+                    std::vector<Subsystem>{}, std::vector<Subsystem>{}, std::set<std::string>{}, "",
+                    false) {}
 
 }  // namespace init
 }  // namespace android
diff --git a/init/devices.h b/init/devices.h
index b8f8e54..69a2449 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <algorithm>
+#include <map>
 #include <set>
 #include <string>
 #include <vector>
@@ -128,7 +129,7 @@
 
     DeviceHandler();
     DeviceHandler(std::vector<Permissions> dev_permissions,
-                  std::vector<SysfsPermissions> sysfs_permissions,
+                  std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> drivers,
                   std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
                   std::string boot_part_uuid, bool skip_restorecon);
     virtual ~DeviceHandler() = default;
@@ -145,6 +146,11 @@
     bool IsBootDevice(const Uevent& uevent) const;
 
   private:
+    struct TrackedUevent {
+        Uevent uevent;
+        std::string canonical_device_path;
+    };
+
     void ColdbootDone() override;
     BlockDeviceInfo GetBlockDeviceInfo(const std::string& uevent_path) const;
     bool FindSubsystemDevice(std::string path, std::string* device_path,
@@ -163,14 +169,21 @@
     void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
     void HandleAshmemUevent(const Uevent& uevent);
 
+    void TrackDeviceUevent(const Uevent& uevent);
+    void HandleBindInternal(std::string driver_name, std::string action, const Uevent& uevent);
+
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
+    std::vector<Subsystem> drivers_;
     std::vector<Subsystem> subsystems_;
     std::set<std::string> boot_devices_;
     std::string boot_part_uuid_;
     bool found_boot_part_uuid_;
     bool skip_restorecon_;
     std::string sysfs_mount_point_;
+
+    std::vector<TrackedUevent> tracked_uevents_;
+    std::map<std::string, std::string> bound_drivers_;
 };
 
 // Exposed for testing
diff --git a/init/uevent.h b/init/uevent.h
index c8ca52a..e7ed226 100644
--- a/init/uevent.h
+++ b/init/uevent.h
@@ -26,6 +26,7 @@
     std::string action;
     std::string path;
     std::string subsystem;
+    std::string driver;
     std::string firmware;
     std::string partition_name;
     std::string partition_uuid;
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 97f3de6..d329c17 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -36,6 +36,7 @@
     uevent->action.clear();
     uevent->path.clear();
     uevent->subsystem.clear();
+    uevent->driver.clear();
     uevent->firmware.clear();
     uevent->partition_name.clear();
     uevent->device_name.clear();
@@ -51,6 +52,9 @@
         } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
             msg += 10;
             uevent->subsystem = msg;
+        } else if (!strncmp(msg, "DRIVER=", 7)) {
+            msg += 7;
+            uevent->driver = msg;
         } else if (!strncmp(msg, "FIRMWARE=", 9)) {
             msg += 9;
             uevent->firmware = msg;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 286e472..cb6b851 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -364,8 +364,8 @@
     std::unique_ptr<DeviceHandler> device_handler = std::make_unique<DeviceHandler>(
             std::move(ueventd_configuration.dev_permissions),
             std::move(ueventd_configuration.sysfs_permissions),
-            std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(),
-            android::fs_mgr::GetBootPartUuid(), true);
+            std::move(ueventd_configuration.drivers), std::move(ueventd_configuration.subsystems),
+            android::fs_mgr::GetBootDevices(), android::fs_mgr::GetBootPartUuid(), true);
     uevent_listener.RegenerateUevents([&](const Uevent& uevent) -> ListenerAction {
         bool uuid_check_done = device_handler->CheckUeventForBootPartUuid(uevent);
         return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 4395d88..097ef09 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -264,6 +264,8 @@
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
     parser.AddSectionParser("subsystem",
                             std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
+    parser.AddSectionParser("driver",
+                            std::make_unique<SubsystemParser>(&ueventd_configuration.drivers));
 
     using namespace std::placeholders;
     parser.AddSingleLineParser(
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 81f4e9d..ffe6072 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -27,6 +27,7 @@
 
 struct UeventdConfiguration {
     std::vector<Subsystem> subsystems;
+    std::vector<Subsystem> drivers;
     std::vector<SysfsPermissions> sysfs_permissions;
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 41924e2..6d91039 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -106,7 +106,32 @@
             {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
             {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
 
-    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}});
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}, {}});
+}
+
+TEST(ueventd_parser, Drivers) {
+    auto ueventd_file = R"(
+driver test_devname
+    devname uevent_devname
+
+driver test_devpath_no_dirname
+    devname uevent_devpath
+
+driver test_devname2
+    devname uevent_devname
+
+driver test_devpath_dirname
+    devname uevent_devpath
+    dirname /dev/graphics
+)";
+
+    auto drivers = std::vector<Subsystem>{
+            {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
+
+    TestUeventdFile(ueventd_file, {{}, drivers, {}, {}, {}, {}, {}, {}});
 }
 
 TEST(ueventd_parser, Permissions) {
@@ -132,7 +157,7 @@
             {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
     };
 
-    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}, {}});
+    TestUeventdFile(ueventd_file, {{}, {}, sysfs_permissions, permissions, {}, {}, {}});
 }
 
 TEST(ueventd_parser, FirmwareDirectories) {
@@ -148,7 +173,7 @@
             "/more",
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}, {}});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, firmware_directories, {}, {}});
 }
 
 TEST(ueventd_parser, ExternalFirmwareHandlers) {
@@ -214,7 +239,7 @@
             },
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, external_firmware_handlers, {}});
 }
 
 TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) {
@@ -232,7 +257,7 @@
             },
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, external_firmware_handlers, {}});
 }
 
 TEST(ueventd_parser, ParallelRestoreconDirs) {
@@ -246,7 +271,7 @@
             "/sys/devices",
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, parallel_restorecon_dirs});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, parallel_restorecon_dirs});
 }
 
 TEST(ueventd_parser, UeventSocketRcvbufSize) {
@@ -255,7 +280,7 @@
 uevent_socket_rcvbuf_size 8M
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
 }
 
 TEST(ueventd_parser, EnabledDisabledLines) {
@@ -265,7 +290,7 @@
 modalias_handling disabled
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 0, true});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, {}, false, 0, true});
 
     auto ueventd_file2 = R"(
 parallel_restorecon enabled
@@ -273,7 +298,7 @@
 parallel_restorecon disabled
 )";
 
-    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, true, 0, false});
+    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, {}, true, 0, false});
 }
 
 TEST(ueventd_parser, AllTogether) {
@@ -286,6 +311,9 @@
 subsystem test_devname
     devname uevent_devname
 
+driver d_test_devpath
+    devname uevent_devpath
+
 /dev/graphics/*           0660   root       graphics
 
 subsystem test_devpath_no_dirname
@@ -303,6 +331,10 @@
     devname uevent_devpath
     dirname /dev/graphics
 
+driver d_test_devname_dirname
+    devname uevent_devname
+    dirname /dev/sound
+
 /dev/*/test               0660   root       system
 /sys/devices/virtual/*/input   poll_delay  0660  root   input    no_fnm_pathname
 firmware_directories /more
@@ -325,6 +357,10 @@
             {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
             {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
 
+    auto drivers = std::vector<Subsystem>{
+            {"d_test_devpath", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"d_test_devname_dirname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev/graphics"}};
+
     auto permissions = std::vector<Permissions>{
             {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false},
             {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false},
@@ -356,7 +392,7 @@
     size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
 
     TestUeventdFile(ueventd_file,
-                    {subsystems, sysfs_permissions, permissions, firmware_directories,
+                    {subsystems, drivers, sysfs_permissions, permissions, firmware_directories,
                      external_firmware_handlers, parallel_restorecon_dirs, true,
                      uevent_socket_rcvbuf_size, true});
 }