init: Factor out first-stage uevent handling into a separate class.

This will make it possible to re-use in second-stage init.

Bug: 150315914
Test: first-stage init boots
Change-Id: I6a0f13d5c71ab5529a76751f68ac0f15834323d1
diff --git a/init/Android.bp b/init/Android.bp
index 72a7bfe..17e4adf 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -36,6 +36,7 @@
     "util.cpp",
 ]
 init_device_sources = [
+    "block_dev_initializer.cpp",
     "bootchart.cpp",
     "builtins.cpp",
     "devices.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index 07b0f95..e647d2b 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -48,6 +48,7 @@
 include $(CLEAR_VARS)
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES := \
+    block_dev_initializer.cpp \
     devices.cpp \
     first_stage_init.cpp \
     first_stage_main.cpp \
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
new file mode 100644
index 0000000..b423f86
--- /dev/null
+++ b/init/block_dev_initializer.cpp
@@ -0,0 +1,148 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <chrono>
+#include <string_view>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+
+#include "block_dev_initializer.h"
+
+namespace android {
+namespace init {
+
+using android::base::Timer;
+using namespace std::chrono_literals;
+
+BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024) {
+    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), false);
+}
+
+bool BlockDevInitializer::InitDeviceMapper() {
+    const std::string dm_path = "/devices/virtual/misc/device-mapper";
+    bool found = false;
+    auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+        if (uevent.path == dm_path) {
+            device_handler_->HandleUevent(uevent);
+            found = true;
+            return ListenerAction::kStop;
+        }
+        return ListenerAction::kContinue;
+    };
+    uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+    if (!found) {
+        LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+        Timer t;
+        uevent_listener_.Poll(dm_callback, 10s);
+        LOG(INFO) << "Wait for device-mapper returned after " << t;
+    }
+    if (!found) {
+        LOG(ERROR) << "device-mapper device not found after polling timeout";
+        return false;
+    }
+    return true;
+}
+
+ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,
+                                                 std::set<std::string>* devices) {
+    // Ignore everything that is not a block device.
+    if (uevent.subsystem != "block") {
+        return ListenerAction::kContinue;
+    }
+
+    auto name = uevent.partition_name;
+    if (name.empty()) {
+        size_t base_idx = uevent.path.rfind('/');
+        if (base_idx == std::string::npos) {
+            return ListenerAction::kContinue;
+        }
+        name = uevent.path.substr(base_idx + 1);
+    }
+
+    auto iter = devices->find(name);
+    if (iter == devices->end()) {
+        return ListenerAction::kContinue;
+    }
+
+    LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
+
+    devices->erase(iter);
+    device_handler_->HandleUevent(uevent);
+    return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue;
+}
+
+bool BlockDevInitializer::InitDevices(std::set<std::string> devices) {
+    auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {
+        return HandleUevent(uevent, &devices);
+    };
+    uevent_listener_.RegenerateUevents(uevent_callback);
+
+    // UeventCallback() will remove found partitions from |devices|. So if it
+    // isn't empty here, it means some partitions are not found.
+    if (!devices.empty()) {
+        LOG(INFO) << __PRETTY_FUNCTION__
+                  << ": partition(s) not found in /sys, waiting for their uevent(s): "
+                  << android::base::Join(devices, ", ");
+        Timer t;
+        uevent_listener_.Poll(uevent_callback, 10s);
+        LOG(INFO) << "Wait for partitions returned after " << t;
+    }
+
+    if (!devices.empty()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+                   << android::base::Join(devices, ", ");
+        return false;
+    }
+    return true;
+}
+
+// Creates "/dev/block/dm-XX" for dm nodes by running coldboot on /sys/block/dm-XX.
+bool BlockDevInitializer::InitDmDevice(const std::string& device) {
+    const std::string device_name(basename(device.c_str()));
+    const std::string syspath = "/sys/block/" + device_name;
+    bool found = false;
+
+    auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
+        if (uevent.device_name == device_name) {
+            LOG(VERBOSE) << "Creating device-mapper device : " << device;
+            device_handler_->HandleUevent(uevent);
+            found = true;
+            return ListenerAction::kStop;
+        }
+        return ListenerAction::kContinue;
+    };
+
+    uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
+    if (!found) {
+        LOG(INFO) << "dm device '" << device << "' not found in /sys, waiting for its uevent";
+        Timer t;
+        uevent_listener_.Poll(uevent_callback, 10s);
+        LOG(INFO) << "wait for dm device '" << device << "' returned after " << t;
+    }
+    if (!found) {
+        LOG(ERROR) << "dm device '" << device << "' not found after polling timeout";
+        return false;
+    }
+    return true;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
new file mode 100644
index 0000000..0d4c6e9
--- /dev/null
+++ b/init/block_dev_initializer.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "devices.h"
+#include "uevent_listener.h"
+
+namespace android {
+namespace init {
+
+class BlockDevInitializer final {
+  public:
+    BlockDevInitializer();
+
+    bool InitDeviceMapper();
+    bool InitDevices(std::set<std::string> devices);
+    bool InitDmDevice(const std::string& device);
+
+  private:
+    ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
+
+    std::unique_ptr<DeviceHandler> device_handler_;
+    UeventListener uevent_listener_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 3451264..8eb2f97 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -42,6 +42,7 @@
 #include <liblp/liblp.h>
 #include <libsnapshot/snapshot.h>
 
+#include "block_dev_initializer.h"
 #include "devices.h"
 #include "switch_root.h"
 #include "uevent.h"
@@ -84,11 +85,7 @@
     bool InitDevices();
 
   protected:
-    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&,
-                                     std::set<std::string>* required_devices);
     bool InitRequiredDevices(std::set<std::string> devices);
-    bool InitMappedDevice(const std::string& verity_device);
-    bool InitDeviceMapper();
     bool CreateLogicalPartitions();
     bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
                         Fstab::iterator* end = nullptr);
@@ -106,8 +103,6 @@
     // revocation check by DSU installation service.
     void CopyDsuAvbKeys();
 
-    ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
-
     // Pure virtual functions.
     virtual bool GetDmVerityDevices(std::set<std::string>* devices) = 0;
     virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
@@ -119,8 +114,7 @@
     // The super path is only set after InitDevices, and is invalid before.
     std::string super_path_;
     std::string super_partition_name_;
-    std::unique_ptr<DeviceHandler> device_handler_;
-    UeventListener uevent_listener_;
+    BlockDevInitializer block_dev_init_;
     // Reads all AVB keys before chroot into /system, as they might be used
     // later when mounting other partitions, e.g., /vendor and /product.
     std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
@@ -234,13 +228,7 @@
 
 // Class Definitions
 // -----------------
-FirstStageMount::FirstStageMount(Fstab fstab)
-    : need_dm_verity_(false), fstab_(std::move(fstab)), uevent_listener_(16 * 1024 * 1024) {
-    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), false);
-
+FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)) {
     super_partition_name_ = fs_mgr_get_super_partition_name();
 }
 
@@ -308,62 +296,13 @@
 // Found partitions will then be removed from it for the subsequent member
 // function to check which devices are NOT created.
 bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
-    if (!InitDeviceMapper()) {
+    if (!block_dev_init_.InitDeviceMapper()) {
         return false;
     }
-
     if (devices.empty()) {
         return true;
     }
-
-    auto uevent_callback = [&, this](const Uevent& uevent) {
-        return UeventCallback(uevent, &devices);
-    };
-    uevent_listener_.RegenerateUevents(uevent_callback);
-
-    // UeventCallback() will remove found partitions from |devices|. So if it
-    // isn't empty here, it means some partitions are not found.
-    if (!devices.empty()) {
-        LOG(INFO) << __PRETTY_FUNCTION__
-                  << ": partition(s) not found in /sys, waiting for their uevent(s): "
-                  << android::base::Join(devices, ", ");
-        Timer t;
-        uevent_listener_.Poll(uevent_callback, 10s);
-        LOG(INFO) << "Wait for partitions returned after " << t;
-    }
-
-    if (!devices.empty()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
-                   << android::base::Join(devices, ", ");
-        return false;
-    }
-
-    return true;
-}
-
-bool FirstStageMount::InitDeviceMapper() {
-    const std::string dm_path = "/devices/virtual/misc/device-mapper";
-    bool found = false;
-    auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
-        if (uevent.path == dm_path) {
-            device_handler_->HandleUevent(uevent);
-            found = true;
-            return ListenerAction::kStop;
-        }
-        return ListenerAction::kContinue;
-    };
-    uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
-    if (!found) {
-        LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
-        Timer t;
-        uevent_listener_.Poll(dm_callback, 10s);
-        LOG(INFO) << "Wait for device-mapper returned after " << t;
-    }
-    if (!found) {
-        LOG(ERROR) << "device-mapper device not found after polling timeout";
-        return false;
-    }
-    return true;
+    return block_dev_init_.InitDevices(std::move(devices));
 }
 
 bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
@@ -419,75 +358,6 @@
     return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
 }
 
-ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent,
-                                                  std::set<std::string>* required_devices) {
-    // Matches partition name to create device nodes.
-    // Both required_devices_partition_names_ and uevent->partition_name have A/B
-    // suffix when A/B is used.
-    auto iter = required_devices->find(name);
-    if (iter != required_devices->end()) {
-        LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
-        required_devices->erase(iter);
-        device_handler_->HandleUevent(uevent);
-        if (required_devices->empty()) {
-            return ListenerAction::kStop;
-        } else {
-            return ListenerAction::kContinue;
-        }
-    }
-    return ListenerAction::kContinue;
-}
-
-ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent,
-                                               std::set<std::string>* required_devices) {
-    // Ignores everything that is not a block device.
-    if (uevent.subsystem != "block") {
-        return ListenerAction::kContinue;
-    }
-
-    if (!uevent.partition_name.empty()) {
-        return HandleBlockDevice(uevent.partition_name, uevent, required_devices);
-    } else {
-        size_t base_idx = uevent.path.rfind('/');
-        if (base_idx != std::string::npos) {
-            return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent, required_devices);
-        }
-    }
-    // Not found a partition or find an unneeded partition, continue to find others.
-    return ListenerAction::kContinue;
-}
-
-// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-bool FirstStageMount::InitMappedDevice(const std::string& dm_device) {
-    const std::string device_name(basename(dm_device.c_str()));
-    const std::string syspath = "/sys/block/" + device_name;
-    bool found = false;
-
-    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_->HandleUevent(uevent);
-            found = true;
-            return ListenerAction::kStop;
-        }
-        return ListenerAction::kContinue;
-    };
-
-    uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
-    if (!found) {
-        LOG(INFO) << "dm device '" << dm_device << "' not found in /sys, waiting for its uevent";
-        Timer t;
-        uevent_listener_.Poll(verity_callback, 10s);
-        LOG(INFO) << "wait for dm device '" << dm_device << "' returned after " << t;
-    }
-    if (!found) {
-        LOG(ERROR) << "dm device '" << dm_device << "' not found after polling timeout";
-        return false;
-    }
-
-    return true;
-}
-
 bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
                                      Fstab::iterator* end) {
     // Sets end to begin + 1, so we can just return on failure below.
@@ -499,7 +369,7 @@
         if (!fs_mgr_update_logical_partition(&(*begin))) {
             return false;
         }
-        if (!InitMappedDevice(begin->blk_device)) {
+        if (!block_dev_init_.InitDmDevice(begin->blk_device)) {
             return false;
         }
     }
@@ -666,7 +536,9 @@
     auto init_devices = [this](std::set<std::string> devices) -> bool {
         for (auto iter = devices.begin(); iter != devices.end();) {
             if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
-                if (!InitMappedDevice(*iter)) return false;
+                if (!block_dev_init_.InitDmDevice(*iter)) {
+                    return false;
+                }
                 iter = devices.erase(iter);
             } else {
                 iter++;
@@ -782,7 +654,7 @@
                 // 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.
-                return InitMappedDevice(fstab_entry->blk_device);
+                return block_dev_init_.InitDmDevice(fstab_entry->blk_device);
             default:
                 return false;
         }
@@ -902,7 +774,7 @@
             // 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.
-            return InitMappedDevice(fstab_entry->blk_device);
+            return block_dev_init_.InitDmDevice(fstab_entry->blk_device);
         default:
             return false;
     }