release-request-5bf29450-4eb0-4d71-a15a-d8858bf184d3-for-git_oc-release-4120128 snap-temp-L37600000076154351

Change-Id: Icfb38e46cb517b62b9af2c95c4bbc2afc4c56084
diff --git a/init/devices.cpp b/init/devices.cpp
index 39571ac..6f86662 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <libgen.h>
+#include <poll.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -46,6 +47,7 @@
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/list.h>
 #include <cutils/uevent.h>
@@ -79,16 +81,8 @@
     struct listnode plist;
 };
 
-struct platform_node {
-    char *name;
-    char *path;
-    int path_len;
-    struct listnode list;
-};
-
 static list_declare(sys_perms);
 static list_declare(dev_perms);
-static list_declare(platform_names);
 
 int add_dev_perms(const char *name, const char *attr,
                   mode_t perm, unsigned int uid, unsigned int gid,
@@ -286,77 +280,37 @@
     }
 }
 
-static void add_platform_device(const char *path)
-{
-    int path_len = strlen(path);
-    struct platform_node *bus;
-    const char *name = path;
+// Given a path that may start with a platform device, find the parent platform device by finding a
+// parent directory with a 'subsystem' symlink that points to the platform bus.
+// If it doesn't start with a platform device, return false
+bool FindPlatformDevice(std::string path, std::string* platform_device_path) {
+    platform_device_path->clear();
 
-    if (!strncmp(path, "/devices/", 9)) {
-        name += 9;
-        if (!strncmp(name, "platform/", 9))
-            name += 9;
-    }
+    static const std::string kSysfsMountPoint = "/sys";
 
-    LOG(VERBOSE) << "adding platform device " << name << " (" << path << ")";
+    // Uevents don't contain the mount point, so we need to add it here.
+    path.insert(0, kSysfsMountPoint);
 
-    bus = (platform_node*) calloc(1, sizeof(struct platform_node));
-    bus->path = strdup(path);
-    bus->path_len = path_len;
-    bus->name = bus->path + (name - path);
-    list_add_tail(&platform_names, &bus->list);
-}
+    std::string directory = android::base::Dirname(path);
 
-/*
- * given a path that may start with a platform device, find the length of the
- * platform device prefix.  If it doesn't start with a platform device, return
- * 0.
- */
-static struct platform_node *find_platform_device(const char *path)
-{
-    int path_len = strlen(path);
-    struct listnode *node;
-    struct platform_node *bus;
-
-    list_for_each_reverse(node, &platform_names) {
-        bus = node_to_item(node, struct platform_node, list);
-        if ((bus->path_len < path_len) &&
-                (path[bus->path_len] == '/') &&
-                !strncmp(path, bus->path, bus->path_len))
-            return bus;
-    }
-
-    return NULL;
-}
-
-static void remove_platform_device(const char *path)
-{
-    struct listnode *node;
-    struct platform_node *bus;
-
-    list_for_each_reverse(node, &platform_names) {
-        bus = node_to_item(node, struct platform_node, list);
-        if (!strcmp(path, bus->path)) {
-            LOG(INFO) << "removing platform device " << bus->name;
-            free(bus->path);
-            list_remove(node);
-            free(bus);
-            return;
+    while (directory != "/" && directory != ".") {
+        std::string subsystem_link_path;
+        if (android::base::Realpath(directory + "/subsystem", &subsystem_link_path) &&
+            subsystem_link_path == kSysfsMountPoint + "/bus/platform") {
+            // We need to remove the mount point that we added above before returning.
+            directory.erase(0, kSysfsMountPoint.size());
+            *platform_device_path = directory;
+            return true;
         }
-    }
-}
 
-static void destroy_platform_devices() {
-    struct listnode* node;
-    struct listnode* n;
-    struct platform_node* bus;
+        auto last_slash = path.rfind('/');
+        if (last_slash == std::string::npos) return false;
 
-    list_for_each_safe(node, n, &platform_names) {
-        list_remove(node);
-        bus = node_to_item(node, struct platform_node, list);
-        free(bus->path);
-        free(bus);
+        path.erase(last_slash);
+        directory = android::base::Dirname(path);
     }
+
+    return false;
 }
 
 /* Given a path that may start with a PCI device, populate the supplied buffer
@@ -480,11 +434,9 @@
     char **links;
     int link_num = 0;
     int width;
-    struct platform_node *pdev;
 
-    pdev = find_platform_device(uevent->path);
-    if (!pdev)
-        return NULL;
+    std::string platform_device;
+    if (!FindPlatformDevice(uevent->path, &platform_device)) return nullptr;
 
     links = (char**) malloc(sizeof(char *) * 2);
     if (!links)
@@ -492,7 +444,7 @@
     memset(links, 0, sizeof(char *) * 2);
 
     /* skip "/devices/platform/<driver>" */
-    parent = strchr(uevent->path + pdev->path_len, '/');
+    parent = strchr(uevent->path + platform_device.size(), '/');
     if (!parent)
         goto err;
 
@@ -527,8 +479,6 @@
 }
 
 char** get_block_device_symlinks(struct uevent* uevent) {
-    const char *device;
-    struct platform_node *pdev;
     const char *slash;
     const char *type;
     char buf[256];
@@ -536,9 +486,18 @@
     int link_num = 0;
     char *p;
 
-    pdev = find_platform_device(uevent->path);
-    if (pdev) {
-        device = pdev->name;
+    std::string device;
+    if (FindPlatformDevice(uevent->path, &device)) {
+        // Skip /devices/platform or /devices/ if present
+        static const std::string devices_platform_prefix = "/devices/platform/";
+        static const std::string devices_prefix = "/devices/";
+
+        if (android::base::StartsWith(device, devices_platform_prefix.c_str())) {
+            device = device.substr(devices_platform_prefix.length());
+        } else if (android::base::StartsWith(device, devices_prefix.c_str())) {
+            device = device.substr(devices_prefix.length());
+        }
+
         type = "platform";
     } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
         device = buf;
@@ -557,7 +516,7 @@
 
     LOG(VERBOSE) << "found " << type << " device " << device;
 
-    snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
+    snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device.c_str());
 
     if (uevent->partition_name) {
         p = strdup(uevent->partition_name);
@@ -635,16 +594,6 @@
     }
 }
 
-static void handle_platform_device_event(struct uevent *uevent)
-{
-    const char *path = uevent->path;
-
-    if (!strcmp(uevent->action, "add"))
-        add_platform_device(path);
-    else if (!strcmp(uevent->action, "remove"))
-        remove_platform_device(path);
-}
-
 static const char *parse_device_name(struct uevent *uevent, unsigned int len)
 {
     const char *name;
@@ -824,8 +773,6 @@
 
     if (!strncmp(uevent->subsystem, "block", 5)) {
         handle_block_device_event(uevent);
-    } else if (!strncmp(uevent->subsystem, "platform", 8)) {
-        handle_platform_device_event(uevent);
     } else {
         handle_generic_device_event(uevent);
     }
@@ -1078,11 +1025,41 @@
 }
 
 void device_close() {
-    destroy_platform_devices();
     device_fd.reset();
     selinux_status_close();
 }
 
-int get_device_fd() {
-    return device_fd;
+void device_poll(const coldboot_callback& callback,
+                 const std::optional<std::chrono::milliseconds> relative_timeout) {
+    using namespace std::chrono;
+
+    pollfd ufd;
+    ufd.events = POLLIN;
+    ufd.fd = device_fd;
+
+    auto start_time = steady_clock::now();
+
+    while (true) {
+        ufd.revents = 0;
+
+        int timeout_ms = -1;
+        if (relative_timeout) {
+            auto now = steady_clock::now();
+            auto time_elapsed = duration_cast<milliseconds>(now - start_time);
+            if (time_elapsed > *relative_timeout) return;
+
+            auto remaining_timeout = *relative_timeout - time_elapsed;
+            timeout_ms = remaining_timeout.count();
+        }
+
+        int nr = poll(&ufd, 1, timeout_ms);
+        if (nr == 0) return;
+        if (nr < 0) {
+            continue;
+        }
+        if (ufd.revents & POLLIN) {
+            auto ret = handle_device_fd(callback);
+            if (should_stop_coldboot(ret)) return;
+        }
+    }
 }
diff --git a/init/devices.h b/init/devices.h
index 3f2cde4..62aef2e 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,9 +17,12 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
-#include <functional>
 #include <sys/stat.h>
 
+#include <chrono>
+#include <functional>
+#include <optional>
+
 enum coldboot_action_t {
     // coldboot continues without creating the device for the uevent
     COLDBOOT_CONTINUE = 0,
@@ -53,8 +56,10 @@
                          mode_t perm, unsigned int uid,
                          unsigned int gid, unsigned short prefix,
                          unsigned short wildcard);
-int get_device_fd();
 
 char** get_block_device_symlinks(struct uevent* uevent);
 
+void device_poll(const coldboot_callback& callback = nullptr,
+                 const std::optional<std::chrono::milliseconds> relative_timeout = {});
+
 #endif	/* _INIT_DEVICES_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index bcc8d1b..9425027 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <memory>
 #include <set>
 #include <string>
@@ -33,6 +34,8 @@
 #include "fs_mgr_avb.h"
 #include "util.h"
 
+using namespace std::chrono_literals;
+
 // Class Declarations
 // ------------------
 class FirstStageMount {
@@ -47,8 +50,8 @@
     bool InitDevices();
 
   protected:
-    void InitRequiredDevices();
-    void InitVerityDevice(const std::string& verity_device);
+    bool InitRequiredDevices();
+    bool InitVerityDevice(const std::string& verity_device);
     bool MountPartitions();
 
     virtual coldboot_action_t ColdbootCallback(uevent* uevent);
@@ -139,50 +142,56 @@
     return true;
 }
 
-bool FirstStageMount::InitDevices() {
-    if (!GetRequiredDevices()) return false;
-
-    InitRequiredDevices();
-
-    // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
-    // So if it isn't empty here, it means some partitions are not found.
-    if (!required_devices_partition_names_.empty()) {
-        LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
-                   << android::base::Join(required_devices_partition_names_, ", ");
-        return false;
-    } else {
-        return true;
-    }
-}
+bool FirstStageMount::InitDevices() { return GetRequiredDevices() && InitRequiredDevices(); }
 
 // Creates devices with uevent->partition_name matching one in the member variable
 // required_devices_partition_names_. Found partitions will then be removed from it
 // for the subsequent member function to check which devices are NOT created.
-void FirstStageMount::InitRequiredDevices() {
+bool FirstStageMount::InitRequiredDevices() {
     if (required_devices_partition_names_.empty()) {
-        return;
+        return true;
     }
 
     if (need_dm_verity_) {
         const std::string dm_path = "/devices/virtual/misc/device-mapper";
-        device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
-            if (uevent->path && uevent->path == dm_path) return COLDBOOT_STOP;
+        bool found = false;
+        auto dm_callback = [&dm_path, &found](uevent* uevent) -> coldboot_action_t {
+            if (uevent->path && uevent->path == dm_path) {
+                found = true;
+                return COLDBOOT_STOP;
+            }
             return COLDBOOT_CONTINUE;  // dm_path not found, continue to find it.
-        });
+        };
+        device_init(("/sys" + dm_path).c_str(), dm_callback);
+        if (!found) {
+            device_poll(dm_callback, 10s);
+        }
+        if (!found) {
+            LOG(ERROR) << "device-mapper device not found";
+            return false;
+        }
     }
 
-    device_init(nullptr,
-                [this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
+    auto uevent_callback = [this](uevent* uevent) -> coldboot_action_t {
+        return ColdbootCallback(uevent);
+    };
+
+    device_init(nullptr, uevent_callback);
+    if (!required_devices_partition_names_.empty()) {
+        device_poll(uevent_callback, 10s);
+    }
+
+    if (!required_devices_partition_names_.empty()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found: "
+                   << android::base::Join(required_devices_partition_names_, ", ");
+        return false;
+    }
 
     device_close();
+    return true;
 }
 
 coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
-    // We need platform devices to create symlinks.
-    if (!strncmp(uevent->subsystem, "platform", 8)) {
-        return COLDBOOT_CREATE;
-    }
-
     // Ignores everything that is not a block device.
     if (strncmp(uevent->subsystem, "block", 5)) {
         return COLDBOOT_CONTINUE;
@@ -208,18 +217,30 @@
 }
 
 // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
     const std::string device_name(basename(verity_device.c_str()));
     const std::string syspath = "/sys/block/" + device_name;
+    bool found = false;
 
-    device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
+    auto verity_callback = [&](uevent* uevent) -> coldboot_action_t {
         if (uevent->device_name && uevent->device_name == device_name) {
             LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+            found = true;
             return COLDBOOT_STOP;
         }
         return COLDBOOT_CONTINUE;
-    });
+    };
+
+    device_init(syspath.c_str(), verity_callback);
+    if (!found) {
+        device_poll(verity_callback, 10s);
+    }
+    if (!found) {
+        LOG(ERROR) << "dm-verity device not found";
+        return false;
+    }
     device_close();
+    return true;
 }
 
 bool FirstStageMount::MountPartitions() {
@@ -285,7 +306,7 @@
         } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
             // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
             // Needs to create it because ueventd isn't started in init first stage.
-            InitVerityDevice(fstab_rec->blk_device);
+            return InitVerityDevice(fstab_rec->blk_device);
         } else {
             return false;
         }
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index f27be64..d0b7b8e 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -17,7 +17,6 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <grp.h>
-#include <poll.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stdio.h>
@@ -76,20 +75,7 @@
 
     device_init();
 
-    pollfd ufd;
-    ufd.events = POLLIN;
-    ufd.fd = get_device_fd();
-
-    while (true) {
-        ufd.revents = 0;
-        int nr = poll(&ufd, 1, -1);
-        if (nr <= 0) {
-            continue;
-        }
-        if (ufd.revents & POLLIN) {
-            handle_device_fd();
-        }
-    }
+    device_poll();
 
     return 0;
 }