Merge "Add the Dwarf CFA handling."
diff --git a/init/Android.mk b/init/Android.mk
index f2c0842..de3d076 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -87,12 +87,12 @@
     bootchart.cpp \
     builtins.cpp \
     init.cpp \
+    init_first_stage.cpp \
     keychords.cpp \
     property_service.cpp \
     reboot.cpp \
     signal_handler.cpp \
     ueventd.cpp \
-    ueventd_parser.cpp \
     watchdogd.cpp \
 
 LOCAL_MODULE:= init
@@ -143,6 +143,7 @@
 LOCAL_SRC_FILES := \
     devices_test.cpp \
     init_parser_test.cpp \
+    init_test.cpp \
     property_service_test.cpp \
     util_test.cpp \
 
@@ -154,7 +155,7 @@
 LOCAL_STATIC_LIBRARIES := libinit
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror -std=gnu++1z
 include $(BUILD_NATIVE_TEST)
 
 
diff --git a/init/README.md b/init/README.md
index 1eb42e0..9fc8d47 100644
--- a/init/README.md
+++ b/init/README.md
@@ -31,13 +31,13 @@
 at the beginning of its execution.  It is responsible for the initial
 set up of the system.
 
-Devices that mount /system, /vendor through the early mount mechanism
+Devices that mount /system, /vendor through the first stage mount mechanism
 load all of the files contained within the
 /{system,vendor,odm}/etc/init/ directories immediately after loading
 the primary /init.rc.  This is explained in more details in the
 Imports section of this file.
 
-Legacy devices without the early mount mechanism do the following:
+Legacy devices without the first stage mount mechanism do the following:
 1. /init.rc imports /init.${ro.hardware}.rc which is the primary
    vendor supplied .rc file.
 2. During the mount\_all command, the init executable loads all of the
@@ -506,7 +506,7 @@
 
    1. When it imports /init.rc or the script indicated by the property
       `ro.boot.init_rc` during initial boot.
-   2. When it imports /{system,vendor,odm}/etc/init/ for early mount
+   2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
       devices immediately after importing /init.rc.
    3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
       paths during mount_all.
@@ -519,7 +519,7 @@
 earlier executed trigger, or 2) place it in an Action with the same
 trigger within the same file at an earlier line.
 
-Nonetheless, the defacto order for early mount devices is:
+Nonetheless, the defacto order for first stage mount devices is:
 1. /init.rc is parsed then recursively each of its imports are
    parsed.
 2. The contents of /system/etc/init/ are alphabetized and parsed
diff --git a/init/action.cpp b/init/action.cpp
index c128968..21abe02 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -58,12 +58,7 @@
         return false;
     }
 
-    if (args.empty()) {
-        *err = "command needed, but not provided";
-        return false;
-    }
-
-    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+    auto function = function_map_->FindFunction(args, err);
     if (!function) {
         return false;
     }
@@ -204,17 +199,19 @@
     return found;
 }
 
-bool Action::CheckEventTrigger(const std::string& trigger) const {
-    return !event_trigger_.empty() &&
-        trigger == event_trigger_ &&
-        CheckPropertyTriggers();
+bool Action::CheckEvent(const EventTrigger& event_trigger) const {
+    return event_trigger == event_trigger_ && CheckPropertyTriggers();
 }
 
-bool Action::CheckPropertyTrigger(const std::string& name,
-                                  const std::string& value) const {
+bool Action::CheckEvent(const PropertyChange& property_change) const {
+    const auto& [name, value] = property_change;
     return event_trigger_.empty() && CheckPropertyTriggers(name, value);
 }
 
+bool Action::CheckEvent(const BuiltinAction& builtin_action) const {
+    return this == builtin_action;
+}
+
 std::string Action::BuildTriggersString() const {
     std::vector<std::string> triggers;
 
@@ -238,41 +235,6 @@
     }
 }
 
-class EventTrigger : public Trigger {
-public:
-    explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
-    }
-    bool CheckTriggers(const Action& action) const override {
-        return action.CheckEventTrigger(trigger_);
-    }
-private:
-    const std::string trigger_;
-};
-
-class PropertyTrigger : public Trigger {
-public:
-    PropertyTrigger(const std::string& name, const std::string& value)
-        : name_(name), value_(value) {
-    }
-    bool CheckTriggers(const Action& action) const override {
-        return action.CheckPropertyTrigger(name_, value_);
-    }
-private:
-    const std::string name_;
-    const std::string value_;
-};
-
-class BuiltinTrigger : public Trigger {
-public:
-    explicit BuiltinTrigger(Action* action) : action_(action) {
-    }
-    bool CheckTriggers(const Action& action) const override {
-        return action_ == &action;
-    }
-private:
-    const Action* action_;
-};
-
 ActionManager::ActionManager() : current_command_(0) {
 }
 
@@ -286,16 +248,15 @@
 }
 
 void ActionManager::QueueEventTrigger(const std::string& trigger) {
-    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+    event_queue_.emplace(trigger);
 }
 
-void ActionManager::QueuePropertyTrigger(const std::string& name,
-                                         const std::string& value) {
-    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+    event_queue_.emplace(std::make_pair(name, value));
 }
 
-void ActionManager::QueueAllPropertyTriggers() {
-    QueuePropertyTrigger("", "");
+void ActionManager::QueueAllPropertyActions() {
+    QueuePropertyChange("", "");
 }
 
 void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
@@ -308,19 +269,20 @@
 
     action->AddCommand(func, name_vector, 0);
 
-    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+    event_queue_.emplace(action.get());
     actions_.emplace_back(std::move(action));
 }
 
 void ActionManager::ExecuteOneCommand() {
-    // Loop through the trigger queue until we have an action to execute
-    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+    // Loop through the event queue until we have an action to execute
+    while (current_executing_actions_.empty() && !event_queue_.empty()) {
         for (const auto& action : actions_) {
-            if (trigger_queue_.front()->CheckTriggers(*action)) {
+            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+                           event_queue_.front())) {
                 current_executing_actions_.emplace(action.get());
             }
         }
-        trigger_queue_.pop();
+        event_queue_.pop();
     }
 
     if (current_executing_actions_.empty()) {
@@ -354,7 +316,7 @@
 }
 
 bool ActionManager::HasMoreCommands() const {
-    return !current_executing_actions_.empty() || !trigger_queue_.empty();
+    return !current_executing_actions_.empty() || !event_queue_.empty();
 }
 
 void ActionManager::DumpState() const {
@@ -363,7 +325,7 @@
     }
 }
 
-bool ActionParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                 int line, std::string* err) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
     if (triggers.size() < 1) {
@@ -380,13 +342,12 @@
     return true;
 }
 
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args, int line,
-                                    std::string* err) {
-    return action_ ? action_->AddCommand(args, line, err) : false;
+bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    return action_ ? action_->AddCommand(std::move(args), line, err) : false;
 }
 
 void ActionParser::EndSection() {
     if (action_ && action_->NumCommands() > 0) {
-        ActionManager::GetInstance().AddAction(std::move(action_));
+        action_manager_->AddAction(std::move(action_));
     }
 }
diff --git a/init/action.h b/init/action.h
index 25e5a3e..d006c50 100644
--- a/init/action.h
+++ b/init/action.h
@@ -20,6 +20,7 @@
 #include <map>
 #include <queue>
 #include <string>
+#include <variant>
 #include <vector>
 
 #include "builtins.h"
@@ -41,6 +42,10 @@
     int line_;
 };
 
+using EventTrigger = std::string;
+using PropertyChange = std::pair<std::string, std::string>;
+using BuiltinAction = class Action*;
+
 class Action {
   public:
     explicit Action(bool oneshot, const std::string& filename, int line);
@@ -52,9 +57,9 @@
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
-    bool CheckEventTrigger(const std::string& trigger) const;
-    bool CheckPropertyTrigger(const std::string& name,
-                              const std::string& value) const;
+    bool CheckEvent(const EventTrigger& event_trigger) const;
+    bool CheckEvent(const PropertyChange& property_change) const;
+    bool CheckEvent(const BuiltinAction& builtin_action) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
 
@@ -81,49 +86,43 @@
     static const KeywordMap<BuiltinFunction>* function_map_;
 };
 
-class Trigger {
-public:
-    virtual ~Trigger() { }
-    virtual bool CheckTriggers(const Action& action) const = 0;
-};
-
 class ActionManager {
-public:
+  public:
     static ActionManager& GetInstance();
 
+    // Exposed for testing
+    ActionManager();
+
     void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
-    void QueuePropertyTrigger(const std::string& name, const std::string& value);
-    void QueueAllPropertyTriggers();
+    void QueuePropertyChange(const std::string& name, const std::string& value);
+    void QueueAllPropertyActions();
     void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
     void ExecuteOneCommand();
     bool HasMoreCommands() const;
     void DumpState() const;
 
-private:
-    ActionManager();
-
+  private:
     ActionManager(ActionManager const&) = delete;
     void operator=(ActionManager const&) = delete;
 
     std::vector<std::unique_ptr<Action>> actions_;
-    std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
     std::queue<const Action*> current_executing_actions_;
     std::size_t current_command_;
 };
 
 class ActionParser : public SectionParser {
   public:
-    ActionParser() : action_(nullptr) {
-    }
-    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+    ActionParser(ActionManager* action_manager)
+        : action_manager_(action_manager), action_(nullptr) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
     void EndSection() override;
-    void EndFile(const std::string&) override {
-    }
 
   private:
+    ActionManager* action_manager_;
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 43eb420..1d98ef1 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -390,7 +390,7 @@
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) parser.DumpState();
+    if (false) DumpState();
 }
 
 /* mount_fstab
@@ -838,7 +838,7 @@
     return e4crypt_do_init_user0();
 }
 
-BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map builtin_functions = {
diff --git a/init/builtins.h b/init/builtins.h
index 53f4a71..e1f0567 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,20 @@
 #ifndef _INIT_BUILTINS_H
 #define _INIT_BUILTINS_H
 
+#include <functional>
 #include <map>
 #include <string>
 #include <vector>
 
 #include "keyword_map.h"
 
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
 class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
-public:
-    BuiltinFunctionMap() {
-    }
-private:
-    Map& map() const override;
+  public:
+    BuiltinFunctionMap() {}
+
+  private:
+    const Map& map() const override;
 };
 
 #endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 6e13863..07d28d0 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -20,8 +20,10 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <fnmatch.h>
+#include <grp.h>
 #include <libgen.h>
 #include <linux/netlink.h>
+#include <pwd.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -49,7 +51,8 @@
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 
-#include "ueventd_parser.h"
+#include "keyword_map.h"
+#include "ueventd.h"
 #include "util.h"
 
 extern struct selabel_handle *sehandle;
@@ -103,6 +106,137 @@
 std::vector<Permissions> dev_permissions;
 std::vector<SysfsPermissions> sysfs_permissions;
 
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
+    if (is_sysfs && args.size() != 5) {
+        *err = "/sys/ lines must have 5 entries";
+        return false;
+    }
+
+    if (!is_sysfs && args.size() != 4) {
+        *err = "/dev/ lines must have 4 entries";
+        return false;
+    }
+
+    auto it = args.begin();
+    const std::string& name = *it++;
+
+    std::string sysfs_attribute;
+    if (is_sysfs) sysfs_attribute = *it++;
+
+    // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+    std::string& perm_string = *it++;
+    char* end_pointer = 0;
+    mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
+    if (end_pointer == nullptr || *end_pointer != '\0') {
+        *err = "invalid mode '" + perm_string + "'";
+        return false;
+    }
+
+    std::string& uid_string = *it++;
+    passwd* pwd = getpwnam(uid_string.c_str());
+    if (!pwd) {
+        *err = "invalid uid '" + uid_string + "'";
+        return false;
+    }
+    uid_t uid = pwd->pw_uid;
+
+    std::string& gid_string = *it++;
+    struct group* grp = getgrnam(gid_string.c_str());
+    if (!grp) {
+        *err = "invalid gid '" + gid_string + "'";
+        return false;
+    }
+    gid_t gid = grp->gr_gid;
+
+    if (is_sysfs) {
+        sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
+    } else {
+        dev_permissions.emplace_back(name, perm, uid, gid);
+    }
+    return true;
+}
+
+// TODO: Move this to be a member variable of a future devices class.
+static std::vector<Subsystem> subsystems;
+
+std::string Subsystem::ParseDevPath(uevent* uevent) const {
+    std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+                              ? uevent->device_name
+                              : android::base::Basename(uevent->path);
+
+    return dir_name_ + "/" + devname;
+}
+
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                   int line, std::string* err) {
+    if (args.size() != 2) {
+        *err = "subsystems must have exactly one name";
+        return false;
+    }
+
+    if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
+        *err = "ignoring duplicate subsystem entry";
+        return false;
+    }
+
+    subsystem_.name_ = args[1];
+
+    return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1] == "uevent_devname") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+        return true;
+    }
+    if (args[1] == "uevent_devpath") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+        return true;
+    }
+
+    *err = "invalid devname '" + args[1] + "'";
+    return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1].front() != '/') {
+        *err = "dirname '" + args[1] + " ' does not start with '/'";
+        return false;
+    }
+
+    subsystem_.dir_name_ = args[1];
+    return true;
+}
+
+bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    using OptionParser =
+        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+    static class OptionParserMap : public KeywordMap<OptionParser> {
+      private:
+        const Map& map() const override {
+            // clang-format off
+            static const Map option_parsers = {
+                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
+                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
+            };
+            // clang-format on
+            return option_parsers;
+        }
+    } parser_map;
+
+    auto parser = parser_map.FindFunction(args, err);
+
+    if (!parser) {
+        return false;
+    }
+
+    return (this->*parser)(std::move(args), err);
+}
+
+void SubsystemParser::EndSection() {
+    subsystems.emplace_back(std::move(subsystem_));
+}
+
 static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
     // upaths omit the "/sys" that paths in this list
     // contain, so we prepend it...
@@ -483,32 +617,9 @@
     // if it's not a /dev device, nothing to do
     if (uevent->major < 0 || uevent->minor < 0) return;
 
-    std::string name = android::base::Basename(uevent->path);
-    ueventd_subsystem* subsystem = ueventd_subsystem_find_by_name(uevent->subsystem.c_str());
-
     std::string devpath;
 
-    if (subsystem) {
-        std::string devname;
-
-        switch (subsystem->devname_src) {
-        case DEVNAME_UEVENT_DEVNAME:
-            devname = uevent->device_name;
-            break;
-
-        case DEVNAME_UEVENT_DEVPATH:
-            devname = name;
-            break;
-
-        default:
-            LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event";
-            return;
-        }
-
-        // TODO: Remove std::string()
-        devpath = std::string(subsystem->dirname) + "/" + devname;
-        mkdir_recursive(android::base::Dirname(devpath), 0755);
-    } else if (android::base::StartsWith(uevent->subsystem, "usb")) {
+    if (android::base::StartsWith(uevent->subsystem, "usb")) {
         if (uevent->subsystem == "usb") {
             if (!uevent->device_name.empty()) {
                 devpath = "/dev/" + uevent->device_name;
@@ -520,15 +631,19 @@
                 int device_id = uevent->minor % 128 + 1;
                 devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
             }
-            mkdir_recursive(android::base::Dirname(devpath), 0755);
         } else {
             // ignore other USB events
             return;
         }
+    } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
+               subsystem != subsystems.end()) {
+        devpath = subsystem->ParseDevPath(uevent);
     } else {
-        devpath = "/dev/" + name;
+        devpath = "/dev/" + android::base::Basename(uevent->path);
     }
 
+    mkdir_recursive(android::base::Dirname(devpath), 0755);
+
     auto links = get_character_device_symlinks(uevent);
 
     handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
diff --git a/init/devices.h b/init/devices.h
index 2cbae66..647b4c4 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -24,6 +24,8 @@
 #include <string>
 #include <vector>
 
+#include "init_parser.h"
+
 enum coldboot_action_t {
     // coldboot continues without creating the device for the uevent
     COLDBOOT_CONTINUE = 0,
@@ -83,9 +85,45 @@
     const std::string attribute_;
 };
 
-extern std::vector<Permissions> dev_permissions;
-extern std::vector<SysfsPermissions> sysfs_permissions;
+class Subsystem {
+  public:
+    friend class SubsystemParser;
 
+    Subsystem() {}
+
+    // Returns the full path for a uevent of a device that is a member of this subsystem,
+    // according to the rules parsed from ueventd.rc
+    std::string ParseDevPath(uevent* uevent) const;
+
+    bool operator==(const std::string& string_name) { return name_ == string_name; }
+
+  private:
+    enum class DevnameSource {
+        DEVNAME_UEVENT_DEVNAME,
+        DEVNAME_UEVENT_DEVPATH,
+    };
+
+    std::string name_;
+    std::string dir_name_ = "/dev";
+    DevnameSource devname_source_;
+};
+
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser() {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
+                      std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+    void EndSection() override;
+
+  private:
+    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+    Subsystem subsystem_;
+};
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs);
 typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
 extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
 extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index f66b2ba..99275e5 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,7 +20,7 @@
 
 #include "util.h"
 
-bool ImportParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                 int line, std::string* err) {
     if (args.size() != 2) {
         *err = "single argument needed for import\n";
@@ -35,16 +35,18 @@
     }
 
     LOG(INFO) << "Added '" << conf_file << "' to import list";
-    imports_.emplace_back(std::move(conf_file));
+    if (filename_.empty()) filename_ = filename;
+    imports_.emplace_back(std::move(conf_file), line);
     return true;
 }
 
-void ImportParser::EndFile(const std::string& filename) {
+void ImportParser::EndFile() {
     auto current_imports = std::move(imports_);
     imports_.clear();
-    for (const auto& s : current_imports) {
-        if (!Parser::GetInstance().ParseConfig(s)) {
-            PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+    for (const auto& [import, line_num] : current_imports) {
+        if (!parser_->ParseConfig(import)) {
+            PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
+                        << "'";
         }
     }
 }
diff --git a/init/import_parser.h b/init/import_parser.h
index e15d555..45cbfad 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -24,20 +24,17 @@
 
 class ImportParser : public SectionParser {
   public:
-    ImportParser()  {
-    }
-    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+    ImportParser(Parser* parser) : parser_(parser) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args, int line,
-                          std::string* err) override {
-        return true;
-    }
-    void EndSection() override {
-    }
-    void EndFile(const std::string& filename) override;
+    void EndFile() override;
 
   private:
-    std::vector<std::string> imports_;
+    Parser* parser_;
+    // Store filename for later error reporting.
+    std::string filename_;
+    // Vector of imports and their line numbers for later error reporting.
+    std::vector<std::pair<std::string, int>> imports_;
 };
 
 #endif
diff --git a/init/init.cpp b/init/init.cpp
index 9a4aa24..99ce5e6 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -54,15 +54,13 @@
 
 #include <fstream>
 #include <memory>
-#include <set>
 #include <vector>
 
 #include "action.h"
 #include "bootchart.h"
 #include "devices.h"
-#include "fs_mgr.h"
-#include "fs_mgr_avb.h"
 #include "import_parser.h"
+#include "init_first_stage.h"
 #include "init_parser.h"
 #include "keychords.h"
 #include "log.h"
@@ -96,6 +94,11 @@
 static std::string wait_prop_name;
 static std::string wait_prop_value;
 
+void DumpState() {
+    ServiceManager::GetInstance().DumpState();
+    ActionManager::GetInstance().DumpState();
+}
+
 void register_epoll_handler(int fd, void (*fn)()) {
     epoll_event ev;
     ev.events = EPOLLIN;
@@ -161,8 +164,8 @@
     // waiting on a property.
     if (name == "sys.powerctl") HandlePowerctlMessage(value);
 
-    if (property_triggers_enabled)
-        ActionManager::GetInstance().QueuePropertyTrigger(name, value);
+    if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+
     if (waiting_for_prop) {
         if (wait_prop_name == name && wait_prop_value == value) {
             wait_prop_name.clear();
@@ -489,71 +492,10 @@
     }
 }
 
-/* Reads the content of device tree file into dt_value.
- * Returns true if the read is success, false otherwise.
- */
-static bool read_dt_file(const std::string& file_name, std::string* dt_value) {
-    if (android::base::ReadFileToString(file_name, dt_value)) {
-        if (!dt_value->empty()) {
-            dt_value->pop_back();  // Trim the trailing '\0' out.
-            return true;
-        }
-    }
-    return false;
-}
-
-static const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
-
-static bool is_dt_value_expected(const std::string& dt_file_suffix,
-                                 const std::string& expected_value) {
-    std::string dt_value;
-    std::string file_name = kAndroidDtDir + dt_file_suffix;
-
-    if (read_dt_file(file_name, &dt_value)) {
-        if (dt_value == expected_value) {
-            return true;
-        }
-    }
-    return false;
-}
-
-static inline bool is_dt_compatible() {
-    return is_dt_value_expected("compatible", "android,firmware");
-}
-
-static inline bool is_dt_fstab_compatible() {
-    return is_dt_value_expected("fstab/compatible", "android,fstab");
-}
-
-static inline bool is_dt_vbmeta_compatible() {
-    return is_dt_value_expected("vbmeta/compatible", "android,vbmeta");
-}
-
-// Gets the vbmeta config from device tree. Specifically, the 'parts' and 'by_name_prefix'.
-// /{
-//     firmware {
-//         android {
-//             vbmeta {
-//                 compatible = "android,vbmeta";
-//                 parts = "vbmeta,boot,system,vendor"
-//                 by_name_prefix="/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
-//             };
-//         };
-//     };
-//  }
-static bool get_vbmeta_config_from_dt(std::string* vbmeta_partitions,
-                                      std::string* device_file_by_name_prefix) {
-    std::string file_name = kAndroidDtDir + "vbmeta/parts";
-    if (!read_dt_file(file_name, vbmeta_partitions)) return false;
-
-    file_name = kAndroidDtDir + "vbmeta/by_name_prefix";
-    if (!read_dt_file(file_name, device_file_by_name_prefix)) return false;
-
-    return true;
-}
-
 static void process_kernel_dt() {
-    if (!is_dt_compatible()) return;
+    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
+        return;
+    }
 
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
     if (!dir) return;
@@ -593,7 +535,7 @@
 static int queue_property_triggers_action(const std::vector<std::string>& args)
 {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
-    ActionManager::GetInstance().QueueAllPropertyTriggers();
+    ActionManager::GetInstance().QueueAllPropertyActions();
     return 0;
 }
 
@@ -958,285 +900,6 @@
     }
 }
 
-// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-static void device_init_dm_device(const std::string& dm_device) {
-    const std::string device_name(basename(dm_device.c_str()));
-    const std::string syspath = "/sys/block/" + device_name;
-
-    device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
-        if (uevent->device_name == device_name) {
-            LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
-            return COLDBOOT_STOP;
-        }
-        return COLDBOOT_CONTINUE;
-    });
-    device_close();
-}
-
-static bool vboot_1_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs) {
-    if (fstab_recs.empty()) return false;
-
-    for (auto rec : fstab_recs) {
-        bool need_create_dm_device = false;
-        if (fs_mgr_is_verified(rec)) {
-            // setup verity and create the dm-XX block device
-            // needed to mount this partition
-            int ret = fs_mgr_setup_verity(rec, false /* wait_for_verity_dev */);
-            if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
-                LOG(INFO) << "verity disabled for '" << rec->mount_point << "'";
-            } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
-                need_create_dm_device = true;
-            } else {
-                PLOG(ERROR) << "early_mount: failed to setup verity for '" << rec->mount_point
-                            << "'";
-                return false;
-            }
-        }
-        if (need_create_dm_device) {
-            // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
-            // Need to create it because ueventd isn't started during early mount.
-            device_init_dm_device(rec->blk_device);
-        }
-        if (fs_mgr_do_mount_one(rec)) {
-            PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static bool vboot_2_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs,
-                                       const std::string& device_file_by_name_prefix) {
-    if (fstab_recs.empty()) return false;
-
-    FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(device_file_by_name_prefix);
-    if (!avb_handle) {
-        LOG(INFO) << "Failed to Open FsManagerAvbHandle";
-        return false;
-    }
-
-    setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
-    for (auto rec : fstab_recs) {
-        bool need_create_dm_device = false;
-        if (fs_mgr_is_avb(rec)) {
-            if (avb_handle->hashtree_disabled()) {
-                LOG(INFO) << "avb hashtree disabled for '" << rec->mount_point << "'";
-            } else if (avb_handle->SetUpAvb(rec, false /* wait_for_verity_dev */)) {
-                need_create_dm_device = true;
-            } else {
-                PLOG(ERROR) << "early_mount: failed to set up AVB on partition: '"
-                            << rec->mount_point << "'";
-                return false;
-            }
-        }
-        if (need_create_dm_device) {
-            // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
-            // Need to create it because ueventd isn't started during early mount.
-            device_init_dm_device(rec->blk_device);
-        }
-        if (fs_mgr_do_mount_one(rec)) {
-            PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static bool mount_early_partitions(const std::vector<fstab_rec*>& fstab_recs,
-                                   const std::string& device_file_by_name_prefix) {
-    if (is_dt_vbmeta_compatible()) {  // AVB (external/avb) is used to setup dm-verity.
-        return vboot_2_0_mount_partitions(fstab_recs, device_file_by_name_prefix);
-    } else {
-        return vboot_1_0_mount_partitions(fstab_recs);
-    }
-}
-
-// Creates devices with uevent->partition_name matching one in the in/out
-// partition_names. Note that the partition_names MUST have A/B suffix
-// when A/B is used. Found partitions will then be removed from the
-// partition_names for caller to check which devices are NOT created.
-static void early_device_init(std::set<std::string>* partition_names) {
-    if (partition_names->empty()) {
-        return;
-    }
-    device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
-        // we need platform devices to create symlinks
-        if (uevent->subsystem == "platform") {
-            return COLDBOOT_CREATE;
-        }
-
-        // Ignore everything that is not a block device
-        if (uevent->subsystem != "block") {
-            return COLDBOOT_CONTINUE;
-        }
-
-        if (!uevent->partition_name.empty()) {
-            // match partition names to create device nodes for partitions
-            // both partition_names and uevent->partition_name have A/B suffix when A/B is used
-            auto iter = partition_names->find(uevent->partition_name);
-            if (iter != partition_names->end()) {
-                LOG(VERBOSE) << "early_mount: found partition: " << *iter;
-                partition_names->erase(iter);
-                if (partition_names->empty()) {
-                    return COLDBOOT_STOP;  // found all partitions, stop coldboot
-                } else {
-                    return COLDBOOT_CREATE;  // create this device and continue to find others
-                }
-            }
-        }
-        // Not found a partition or find an unneeded partition, continue to find others
-        return COLDBOOT_CONTINUE;
-    });
-}
-
-static bool vboot_1_0_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
-                                       std::set<std::string>* out_partitions,
-                                       bool* out_need_verity) {
-    std::string meta_partition;
-    for (auto fstab_rec : early_fstab_recs) {
-        // don't allow verifyatboot for early mounted partitions
-        if (fs_mgr_is_verifyatboot(fstab_rec)) {
-            LOG(ERROR) << "early_mount: partitions can't be verified at boot";
-            return false;
-        }
-        // check for verified partitions
-        if (fs_mgr_is_verified(fstab_rec)) {
-            *out_need_verity = true;
-        }
-        // check if verity metadata is on a separate partition and get partition
-        // name from the end of the ->verity_loc path. verity state is not partition
-        // specific, so there must be only 1 additional partition that carries
-        // verity state.
-        if (fstab_rec->verity_loc) {
-            if (!meta_partition.empty()) {
-                LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
-                           << ", " << basename(fstab_rec->verity_loc);
-                return false;
-            } else {
-                meta_partition = basename(fstab_rec->verity_loc);
-            }
-        }
-    }
-
-    // includes those early mount partitions and meta_partition (if any)
-    // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
-    for (auto fstab_rec : early_fstab_recs) {
-        out_partitions->emplace(basename(fstab_rec->blk_device));
-    }
-
-    if (!meta_partition.empty()) {
-        out_partitions->emplace(std::move(meta_partition));
-    }
-
-    return true;
-}
-
-// a.k.a. AVB (external/avb)
-static bool vboot_2_0_early_partitions(std::set<std::string>* out_partitions, bool* out_need_verity,
-                                       std::string* out_device_file_by_name_prefix) {
-    std::string vbmeta_partitions;
-    if (!get_vbmeta_config_from_dt(&vbmeta_partitions, out_device_file_by_name_prefix)) {
-        return false;
-    }
-    // libavb verifies AVB metadata on all verified partitions at once.
-    // e.g., The vbmeta_partitions will be "vbmeta,boot,system,vendor"
-    // for libavb to verify metadata, even if we only need to early mount /vendor.
-    std::vector<std::string> partitions = android::base::Split(vbmeta_partitions, ",");
-    std::string ab_suffix = fs_mgr_get_slot_suffix();
-    for (const auto& partition : partitions) {
-        out_partitions->emplace(partition + ab_suffix);
-    }
-    *out_need_verity = true;
-    return true;
-}
-
-static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
-                                 std::set<std::string>* out_partitions, bool* out_need_verity,
-                                 std::string* out_device_file_by_name_prefix) {
-    *out_need_verity = false;
-    out_partitions->clear();
-    out_device_file_by_name_prefix->clear();
-
-    if (is_dt_vbmeta_compatible()) {  // AVB (external/avb) is used to setup dm-verity.
-        return vboot_2_0_early_partitions(out_partitions, out_need_verity,
-                                          out_device_file_by_name_prefix);
-    } else {
-        return vboot_1_0_early_partitions(early_fstab_recs, out_partitions, out_need_verity);
-    }
-}
-
-/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
-static bool early_mount() {
-    // skip early mount if we're in recovery mode
-    if (access("/sbin/recovery", F_OK) == 0) {
-        LOG(INFO) << "Early mount skipped (recovery mode)";
-        return true;
-    }
-
-    // first check if device tree fstab entries are compatible
-    if (!is_dt_fstab_compatible()) {
-        LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
-        return true;
-    }
-
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
-        fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
-    if (!tab) {
-        LOG(ERROR) << "Early mount failed to read fstab from device tree";
-        return false;
-    }
-
-    // find out fstab records for odm, system and vendor
-    std::vector<fstab_rec*> early_fstab_recs;
-    for (auto mount_point : {"/odm", "/system", "/vendor"}) {
-        fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
-        if (fstab_rec != nullptr) {
-            early_fstab_recs.push_back(fstab_rec);
-        }
-    }
-
-    // nothing to early mount
-    if (early_fstab_recs.empty()) return true;
-
-    bool need_verity;
-    std::string device_file_by_name_prefix;
-    std::set<std::string> partition_names;
-    // partition_names MUST have A/B suffix when A/B is used
-    if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity,
-                              &device_file_by_name_prefix)) {
-        return false;
-    }
-
-    bool success = false;
-    // create the devices we need..
-    early_device_init(&partition_names);
-
-    // early_device_init will remove found partitions from partition_names
-    // So if the partition_names is not empty here, means some partitions
-    // are not found
-    if (!partition_names.empty()) {
-        LOG(ERROR) << "early_mount: partition(s) not found: "
-                   << android::base::Join(partition_names, ", ");
-        goto done;
-    }
-
-    if (need_verity) {
-        // create /dev/device mapper
-        device_init("/sys/devices/virtual/misc/device-mapper",
-                    [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
-    }
-
-    if (mount_early_partitions(early_fstab_recs, device_file_by_name_prefix)) {
-        success = true;
-    }
-
-done:
-    device_close();
-    return success;
-}
-
 static void install_reboot_signal_handlers() {
     // Instead of panic'ing the kernel as is the default behavior when init crashes,
     // we prefer to reboot to bootloader on development builds, as this will prevent
@@ -1315,11 +978,13 @@
 
         LOG(INFO) << "init first stage started!";
 
-        if (!early_mount()) {
+        if (!DoFirstStageMount()) {
             LOG(ERROR) << "Failed to mount required partitions early ...";
             panic();
         }
 
+        SetInitAvbVersionInRecovery();
+
         // Set up SELinux, loading the SELinux policy.
         selinux_initialize(true);
 
@@ -1398,10 +1063,13 @@
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceManager& sm = ServiceManager::GetInstance();
     Parser& parser = Parser::GetInstance();
-    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
-    parser.AddSectionParser("on", std::make_unique<ActionParser>());
-    parser.AddSectionParser("import", std::make_unique<ImportParser>());
+
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
     std::string bootscript = GetProperty("ro.boot.init_rc", "");
     if (bootscript.empty()) {
         parser.ParseConfig("/init.rc");
@@ -1419,9 +1087,7 @@
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) parser.DumpState();
-
-    ActionManager& am = ActionManager::GetInstance();
+    if (false) DumpState();
 
     am.QueueEventTrigger("early-init");
 
@@ -1456,10 +1122,10 @@
         // By default, sleep until something happens.
         int epoll_timeout_ms = -1;
 
-        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
             am.ExecuteOneCommand();
         }
-        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
             restart_processes();
 
             // If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index 1da3350..6add75f 100644
--- a/init/init.h
+++ b/init/init.h
@@ -34,4 +34,6 @@
 
 bool start_waiting_for_property(const char *name, const char *value);
 
+void DumpState();
+
 #endif  /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
new file mode 100644
index 0000000..0bacd9c
--- /dev/null
+++ b/init/init_first_stage.cpp
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2017 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 "init_first_stage.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "devices.h"
+#include "fs_mgr.h"
+#include "fs_mgr_avb.h"
+#include "util.h"
+
+// Class Declarations
+// ------------------
+class FirstStageMount {
+  public:
+    FirstStageMount();
+    virtual ~FirstStageMount() = default;
+
+    // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
+    // based on device tree configurations.
+    static std::unique_ptr<FirstStageMount> Create();
+    bool DoFirstStageMount();  // Mounts fstab entries read from device tree.
+    bool InitDevices();
+
+  protected:
+    void InitRequiredDevices(std::set<std::string>* devices_partition_names);
+    void InitVerityDevice(const std::string& verity_device);
+    bool MountPartitions();
+
+    virtual bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+                                    bool* out_need_dm_verity) = 0;
+    virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+
+    // Device tree fstab entries.
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+    // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
+    std::vector<fstab_rec*> mount_fstab_recs_;
+};
+
+class FirstStageMountVBootV1 : public FirstStageMount {
+  public:
+    FirstStageMountVBootV1() = default;
+    ~FirstStageMountVBootV1() override = default;
+
+  protected:
+    bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+                            bool* out_need_dm_verity) override;
+    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+};
+
+class FirstStageMountVBootV2 : public FirstStageMount {
+  public:
+    FirstStageMountVBootV2();
+    ~FirstStageMountVBootV2() override = default;
+
+    const std::string& by_name_prefix() const { return device_tree_by_name_prefix_; }
+
+  protected:
+    bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+                            bool* out_need_dm_verity) override;
+    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+    bool InitAvbHandle();
+
+    std::string device_tree_vbmeta_parts_;
+    std::string device_tree_by_name_prefix_;
+    FsManagerAvbUniquePtr avb_handle_;
+};
+
+// Static Functions
+// ----------------
+static inline bool IsDtVbmetaCompatible() {
+    return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
+}
+
+static bool inline IsRecoveryMode() {
+    return access("/sbin/recovery", F_OK) == 0;
+}
+
+// Class Definitions
+// -----------------
+FirstStageMount::FirstStageMount() : device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
+    if (!device_tree_fstab_) {
+        LOG(ERROR) << "Failed to read fstab from device tree";
+        return;
+    }
+    for (auto mount_point : {"/system", "/vendor", "/odm"}) {
+        fstab_rec* fstab_rec =
+            fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
+        if (fstab_rec != nullptr) {
+            mount_fstab_recs_.push_back(fstab_rec);
+        }
+    }
+}
+
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+    if (IsDtVbmetaCompatible()) {
+        return std::make_unique<FirstStageMountVBootV2>();
+    } else {
+        return std::make_unique<FirstStageMountVBootV1>();
+    }
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+    // Nothing to mount.
+    if (mount_fstab_recs_.empty()) return true;
+
+    if (!InitDevices()) return false;
+
+    if (!MountPartitions()) return false;
+
+    return true;
+}
+
+bool FirstStageMount::InitDevices() {
+    bool need_dm_verity;
+    std::set<std::string> devices_partition_names;
+
+    // The partition name in devices_partition_names MUST have A/B suffix when A/B is used.
+    if (!GetRequiredDevices(&devices_partition_names, &need_dm_verity)) return false;
+
+    if (need_dm_verity) {
+        device_init("/sys/devices/virtual/misc/device-mapper",
+                    [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
+    }
+
+    bool success = false;
+    InitRequiredDevices(&devices_partition_names);
+
+    // InitRequiredDevices() will remove found partitions from devices_partition_names.
+    // So if it isn't empty here, it means some partitions are not found.
+    if (!devices_partition_names.empty()) {
+        LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
+                   << android::base::Join(devices_partition_names, ", ");
+    } else {
+        success = true;
+    }
+
+    device_close();
+    return success;
+}
+
+// Creates devices with uevent->partition_name matching one in the in/out
+// devices_partition_names. Found partitions will then be removed from the
+// devices_partition_names for the caller to check which devices are NOT created.
+void FirstStageMount::InitRequiredDevices(std::set<std::string>* devices_partition_names) {
+    if (devices_partition_names->empty()) {
+        return;
+    }
+    device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
+        // We need platform devices to create symlinks.
+        if (uevent->subsystem == "platform") {
+            return COLDBOOT_CREATE;
+        }
+
+        // Ignores everything that is not a block device.
+        if (uevent->subsystem != "block") {
+            return COLDBOOT_CONTINUE;
+        }
+
+        if (!uevent->partition_name.empty()) {
+            // Matches partition name to create device nodes.
+            // Both devices_partition_names and uevent->partition_name have A/B
+            // suffix when A/B is used.
+            auto iter = devices_partition_names->find(uevent->partition_name);
+            if (iter != devices_partition_names->end()) {
+                LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+                devices_partition_names->erase(iter);
+                if (devices_partition_names->empty()) {
+                    return COLDBOOT_STOP;  // Found all partitions, stop coldboot.
+                } else {
+                    return COLDBOOT_CREATE;  // Creates this device and continue to find others.
+                }
+            }
+        }
+        // Not found a partition or find an unneeded partition, continue to find others.
+        return COLDBOOT_CONTINUE;
+    });
+}
+
+// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
+void 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;
+
+    device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
+        if (uevent->device_name == device_name) {
+            LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+            return COLDBOOT_STOP;
+        }
+        return COLDBOOT_CONTINUE;
+    });
+    device_close();
+}
+
+bool FirstStageMount::MountPartitions() {
+    for (auto fstab_rec : mount_fstab_recs_) {
+        if (!SetUpDmVerity(fstab_rec)) {
+            PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+            return false;
+        }
+        if (fs_mgr_do_mount_one(fstab_rec)) {
+            PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool FirstStageMountVBootV1::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+                                                bool* out_need_dm_verity) {
+    std::string meta_partition;
+    *out_need_dm_verity = false;
+
+    for (auto fstab_rec : mount_fstab_recs_) {
+        // Don't allow verifyatboot in the first stage.
+        if (fs_mgr_is_verifyatboot(fstab_rec)) {
+            LOG(ERROR) << "Partitions can't be verified at boot";
+            return false;
+        }
+        // Checks for verified partitions.
+        if (fs_mgr_is_verified(fstab_rec)) {
+            *out_need_dm_verity = true;
+        }
+        // Checks if verity metadata is on a separate partition and get partition
+        // name from the end of the ->verity_loc path. Verity state is not partition
+        // specific, so there must be only one additional partition that carries
+        // verity state.
+        if (fstab_rec->verity_loc) {
+            if (meta_partition.empty()) {
+                meta_partition = basename(fstab_rec->verity_loc);
+            } else if (meta_partition != fstab_rec->verity_loc) {
+                LOG(ERROR) << "More than one meta partition found: " << meta_partition << ", "
+                           << basename(fstab_rec->verity_loc);
+                return false;
+            }
+        }
+    }
+
+    // Includes those fstab partitions and meta_partition (if any).
+    // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+    for (auto fstab_rec : mount_fstab_recs_) {
+        out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+    }
+
+    if (!meta_partition.empty()) {
+        out_devices_partition_names->emplace(std::move(meta_partition));
+    }
+
+    return true;
+}
+
+bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
+    if (fs_mgr_is_verified(fstab_rec)) {
+        int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+        if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
+            LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
+        } 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);
+        } else {
+            return false;
+        }
+    }
+    return true;  // Returns true to mount the partition.
+}
+
+// FirstStageMountVBootV2 constructor.
+// Gets the vbmeta configurations from device tree.
+// Specifically, the 'parts' and 'by_name_prefix' below.
+// /{
+//     firmware {
+//         android {
+//             vbmeta {
+//                 compatible = "android,vbmeta";
+//                 parts = "vbmeta,boot,system,vendor"
+//                 by_name_prefix = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
+//             };
+//         };
+//     };
+//  }
+FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
+    if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
+        PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
+        return;
+    }
+
+    // TODO: removes by_name_prefix to allow partitions on different block devices.
+    if (!read_android_dt_file("vbmeta/by_name_prefix", &device_tree_by_name_prefix_)) {
+        PLOG(ERROR) << "Failed to read vbmeta/by_name_prefix from dt";
+        return;
+    }
+}
+
+bool FirstStageMountVBootV2::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+                                                bool* out_need_dm_verity) {
+    *out_need_dm_verity = false;
+
+    // fstab_rec->blk_device has A/B suffix.
+    for (auto fstab_rec : mount_fstab_recs_) {
+        if (fs_mgr_is_avb(fstab_rec)) {
+            *out_need_dm_verity = true;
+        }
+        out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+    }
+
+    // libavb verifies AVB metadata on all verified partitions at once.
+    // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
+    // for libavb to verify metadata, even if there is only /vendor in the
+    // above mount_fstab_recs_.
+    if (*out_need_dm_verity) {
+        if (device_tree_vbmeta_parts_.empty()) {
+            LOG(ERROR) << "Missing vbmeta parts in device tree";
+            return false;
+        }
+        std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
+        std::string ab_suffix = fs_mgr_get_slot_suffix();
+        for (const auto& partition : partitions) {
+            // out_devices_partition_names is of type std::set so it's not an issue to emplace
+            // a partition twice. e.g., /vendor might be in both places:
+            //   - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
+            //   - mount_fstab_recs_: /vendor_a
+            out_devices_partition_names->emplace(partition + ab_suffix);
+        }
+    }
+    return true;
+}
+
+bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
+    if (fs_mgr_is_avb(fstab_rec)) {
+        if (!InitAvbHandle()) return false;
+        if (avb_handle_->hashtree_disabled()) {
+            LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
+        } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
+            // 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);
+        } else {
+            return false;
+        }
+    }
+    return true;  // Returns true to mount the partition.
+}
+
+bool FirstStageMountVBootV2::InitAvbHandle() {
+    if (avb_handle_) return true;  // Returns true if the handle is already initialized.
+
+    avb_handle_ = FsManagerAvbHandle::Open(device_tree_by_name_prefix_);
+    if (!avb_handle_) {
+        PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+        return false;
+    }
+    // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
+    setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
+    return true;
+}
+
+// Public functions
+// ----------------
+// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+bool DoFirstStageMount() {
+    // Skips first stage mount if we're in recovery mode.
+    if (IsRecoveryMode()) {
+        LOG(INFO) << "First stage mount skipped (recovery mode)";
+        return true;
+    }
+
+    // Firstly checks if device tree fstab entries are compatible.
+    if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
+        LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
+        return true;
+    }
+
+    std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
+    if (!handle) {
+        LOG(ERROR) << "Failed to create FirstStageMount";
+        return false;
+    }
+    return handle->DoFirstStageMount();
+}
+
+void SetInitAvbVersionInRecovery() {
+    if (!IsRecoveryMode()) {
+        LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
+        return;
+    }
+
+    if (!IsDtVbmetaCompatible()) {
+        LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
+        return;
+    }
+
+    // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+    // to verify AVB metadata on all partitions in the verified chain.
+    // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
+    // Open() function returns a valid handle.
+    // We don't need to mount partitions here in recovery mode.
+    FirstStageMountVBootV2 avb_first_mount;
+    if (!avb_first_mount.InitDevices()) {
+        LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
+        return;
+    }
+
+    FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(avb_first_mount.by_name_prefix());
+    if (!avb_handle) {
+        PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+        return;
+    }
+    setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
+}
diff --git a/init/ueventd_parser.h b/init/init_first_stage.h
similarity index 60%
rename from init/ueventd_parser.h
rename to init/init_first_stage.h
index 4d69897..170a24c 100644
--- a/init/ueventd_parser.h
+++ b/init/init_first_stage.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,15 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_UEVENTD_PARSER_H_
-#define _INIT_UEVENTD_PARSER_H_
+#ifndef _INIT_FIRST_STAGE_H
+#define _INIT_FIRST_STAGE_H
 
-#include "ueventd.h"
-
-#define UEVENTD_PARSER_MAXARGS 5
-
-int ueventd_parse_config_file(const char *fn);
-void set_device_permission(const char* fn, int line, int nargs, char **args);
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
+bool DoFirstStageMount();
+void SetInitAvbVersionInRecovery();
 
 #endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index b425497..620367a 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -17,14 +17,13 @@
 #include "init_parser.h"
 
 #include <dirent.h>
-#include <fcntl.h>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
-#include "action.h"
 #include "parser.h"
-#include "service.h"
+#include "util.h"
 
 Parser::Parser() {
 }
@@ -39,13 +38,16 @@
     section_parsers_[name] = std::move(parser);
 }
 
+void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
+    line_callbacks_.emplace_back(prefix, callback);
+}
+
 void Parser::ParseData(const std::string& filename, const std::string& data) {
     //TODO: Use a parser with const input and remove this copy
     std::vector<char> data_copy(data.begin(), data.end());
     data_copy.push_back('\0');
 
     parse_state state;
-    state.filename = filename.c_str();
     state.line = 0;
     state.ptr = &data_copy[0];
     state.nexttoken = 0;
@@ -65,20 +67,34 @@
             if (args.empty()) {
                 break;
             }
+            // If we have a line matching a prefix we recognize, call its callback and unset any
+            // current section parsers.  This is meant for /sys/ and /dev/ line entries for uevent.
+            for (const auto& [prefix, callback] : line_callbacks_) {
+                if (android::base::StartsWith(args[0], prefix.c_str())) {
+                    if (section_parser) section_parser->EndSection();
+
+                    std::string ret_err;
+                    if (!callback(std::move(args), &ret_err)) {
+                        LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+                    }
+                    section_parser = nullptr;
+                    break;
+                }
+            }
             if (section_parsers_.count(args[0])) {
                 if (section_parser) {
                     section_parser->EndSection();
                 }
                 section_parser = section_parsers_[args[0]].get();
                 std::string ret_err;
-                if (!section_parser->ParseSection(args, state.filename, state.line, &ret_err)) {
-                    parse_error(&state, "%s\n", ret_err.c_str());
+                if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
+                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
                     section_parser = nullptr;
                 }
             } else if (section_parser) {
                 std::string ret_err;
-                if (!section_parser->ParseLineSection(args, state.line, &ret_err)) {
-                    parse_error(&state, "%s\n", ret_err.c_str());
+                if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
+                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
                 }
             }
             args.clear();
@@ -100,8 +116,8 @@
 
     data.push_back('\n'); // TODO: fix parse_config.
     ParseData(path, data);
-    for (const auto& sp : section_parsers_) {
-        sp.second->EndFile(path);
+    for (const auto& [section_name, section_parser] : section_parsers_) {
+        section_parser->EndFile();
     }
 
     LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
@@ -141,8 +157,3 @@
     }
     return ParseConfigFile(path);
 }
-
-void Parser::DumpState() const {
-    ServiceManager::GetInstance().DumpState();
-    ActionManager::GetInstance().DumpState();
-}
diff --git a/init/init_parser.h b/init/init_parser.h
index 64f0cac..bd8a178 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -22,46 +22,72 @@
 #include <string>
 #include <vector>
 
+//  SectionParser is an interface that can parse a given 'section' in init.
+//
+//  You can implement up to 4 functions below, with ParseSection() being mandatory.
+//  The first two function return bool with false indicating a failure and has a std::string* err
+//  parameter into which an error string can be written.  It will be reported along with the
+//  filename and line number of where the error occurred.
+//
+//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+//                       int line, std::string* err)
+//    This function is called when a section is first encountered.
+//
+//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+//    This function is called on each subsequent line until the next section is encountered.
+//
+//  3) bool EndSection()
+//    This function is called either when a new section is found or at the end of the file.
+//    It indicates that parsing of the current section is complete and any relevant objects should
+//    be committed.
+//
+//  4) bool EndFile()
+//    This function is called at the end of the file.
+//    It indicates that the parsing has completed and any relevant objects should be committed.
+
 class SectionParser {
-public:
-    virtual ~SectionParser() {
-    }
-    virtual bool ParseSection(const std::vector<std::string>& args, const std::string& filename,
+  public:
+    virtual ~SectionParser() {}
+    virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line, std::string* err) = 0;
-    virtual bool ParseLineSection(const std::vector<std::string>& args, int line,
-                                  std::string* err) = 0;
-    virtual void EndSection() = 0;
-    virtual void EndFile(const std::string& filename) = 0;
+    virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+    virtual void EndSection(){};
+    virtual void EndFile(){};
 };
 
 class Parser {
-public:
+  public:
+    //  LineCallback is the type for callbacks that can parse a line starting with a given prefix.
+    //
+    //  They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
+    //
+    //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false
+    //  indicating a failure and has an std::string* err parameter into which an error string can
+    //  be written.
+    using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
+
     static Parser& GetInstance();
-    void DumpState() const;
+
+    // Exposed for testing
+    Parser();
+
     bool ParseConfig(const std::string& path);
-    void AddSectionParser(const std::string& name,
-                          std::unique_ptr<SectionParser> parser);
-    void set_is_system_etc_init_loaded(bool loaded) {
-        is_system_etc_init_loaded_ = loaded;
-    }
-    void set_is_vendor_etc_init_loaded(bool loaded) {
-        is_vendor_etc_init_loaded_ = loaded;
-    }
-    void set_is_odm_etc_init_loaded(bool loaded) {
-        is_odm_etc_init_loaded_ = loaded;
-    }
+    void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+    void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+    void set_is_system_etc_init_loaded(bool loaded) { is_system_etc_init_loaded_ = loaded; }
+    void set_is_vendor_etc_init_loaded(bool loaded) { is_vendor_etc_init_loaded_ = loaded; }
+    void set_is_odm_etc_init_loaded(bool loaded) { is_odm_etc_init_loaded_ = loaded; }
     bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
     bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
     bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
 
-private:
-    Parser();
-
+  private:
     void ParseData(const std::string& filename, const std::string& data);
     bool ParseConfigFile(const std::string& path);
     bool ParseConfigDir(const std::string& path);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+    std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
     bool is_system_etc_init_loaded_ = false;
     bool is_vendor_etc_init_loaded_ = false;
     bool is_odm_etc_init_loaded_ = false;
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..3da14b5
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 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 <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "action.h"
+#include "builtins.h"
+#include "import_parser.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
+
+class TestFunctionMap : public KeywordMap<BuiltinFunction> {
+  public:
+    // Helper for argument-less functions
+    using BuiltinFunctionNoArgs = std::function<void(void)>;
+    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+        Add(name, 0, 0, [function](const std::vector<std::string>&) {
+            function();
+            return 0;
+        });
+    }
+
+    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+             const BuiltinFunction function) {
+        builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
+    }
+
+  private:
+    Map builtin_functions_ = {};
+
+    const Map& map() const override { return builtin_functions_; }
+};
+
+using ActionManagerCommand = std::function<void(ActionManager&)>;
+
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+              const std::vector<ActionManagerCommand>& commands) {
+    ActionManager am;
+
+    Action::set_function_map(&test_function_map);
+
+    Parser parser;
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+    ASSERT_TRUE(parser.ParseConfig(init_script_file));
+
+    for (const auto& command : commands) {
+        command(am);
+    }
+
+    while (am.HasMoreCommands()) {
+        am.ExecuteOneCommand();
+    }
+}
+
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+                  const std::vector<ActionManagerCommand>& commands) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+    TestInit(tf.path, test_function_map, commands);
+}
+
+TEST(init, SimpleEventTrigger) {
+    bool expect_true = false;
+    std::string init_script =
+        R"init(
+on boot
+pass_test
+)init";
+
+    TestFunctionMap test_function_map;
+    test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInitText(init_script, test_function_map, commands);
+
+    EXPECT_TRUE(expect_true);
+}
+
+TEST(init, EventTriggerOrder) {
+    std::string init_script =
+        R"init(
+on boot
+execute_first
+
+on boot && property:ro.hardware=*
+execute_second
+
+on boot
+execute_third
+
+)init";
+
+    int num_executed = 0;
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+    test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+    test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInitText(init_script, test_function_map, commands);
+}
+
+TEST(init, EventTriggerOrderMultipleFiles) {
+    // 6 total files, which should have their triggers executed in the following order:
+    // 1: start - original script parsed
+    // 2: first_import - immediately imported by first_script
+    // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
+    // 4: a_import - file imported by dir_a
+    // 5: dir_b - file named 'b.rc' in dir
+    // 6: last_import - imported after dir is imported
+
+    TemporaryFile first_import;
+    ASSERT_TRUE(first_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
+
+    TemporaryFile dir_a_import;
+    ASSERT_TRUE(dir_a_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
+
+    TemporaryFile last_import;
+    ASSERT_TRUE(last_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
+
+    TemporaryDir dir;
+    // clang-format off
+    std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
+                               "on boot\n"
+                               "execute 3";
+    // clang-format on
+    // write_file() ensures the right mode is set
+    ASSERT_TRUE(write_file(std::string(dir.path) + "/a.rc", dir_a_script));
+
+    ASSERT_TRUE(write_file(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
+
+    // clang-format off
+    std::string start_script = "import " + std::string(first_import.path) + "\n"
+                               "import " + std::string(dir.path) + "\n"
+                               "import " + std::string(last_import.path) + "\n"
+                               "on boot\n"
+                               "execute 1";
+    // clang-format on
+    TemporaryFile start;
+    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+    int num_executed = 0;
+    auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+        EXPECT_EQ(2U, args.size());
+        EXPECT_EQ(++num_executed, std::stoi(args[1]));
+        return 0;
+    };
+
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute", 1, 1, execute_command);
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInit(start.path, test_function_map, commands);
+
+    EXPECT_EQ(6, num_executed);
+}
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..88bad01 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -24,18 +24,23 @@
 
 template <typename Function>
 class KeywordMap {
-public:
+  public:
     using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
-    using Map = const std::map<std::string, FunctionInfo>;
+    using Map = std::map<std::string, FunctionInfo>;
 
     virtual ~KeywordMap() {
     }
 
-    const Function FindFunction(const std::string& keyword,
-                                size_t num_args,
-                                std::string* err) const {
+    const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
         using android::base::StringPrintf;
 
+        if (args.empty()) {
+            *err = "keyword needed, but not provided";
+            return nullptr;
+        }
+        auto& keyword = args[0];
+        auto num_args = args.size() - 1;
+
         auto function_info_it = map().find(keyword);
         if (function_info_it == map().end()) {
             *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
@@ -68,10 +73,10 @@
         return std::get<Function>(function_info);
     }
 
-private:
-//Map of keyword ->
-//(minimum number of arguments, maximum number of arguments, function pointer)
-    virtual Map& map() const = 0;
+  private:
+    // Map of keyword ->
+    // (minimum number of arguments, maximum number of arguments, function pointer)
+    virtual const Map& map() const = 0;
 };
 
 #endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 5953a88..0d13cfe 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,28 +1,5 @@
 #include "parser.h"
 
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <android-base/logging.h>
-
-void parse_error(struct parse_state *state, const char *fmt, ...)
-{
-    va_list ap;
-    char buf[128];
-    int off;
-
-    snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
-    buf[127] = 0;
-    off = strlen(buf);
-
-    va_start(ap, fmt);
-    vsnprintf(buf + off, 128 - off, fmt, ap);
-    va_end(ap);
-    buf[127] = 0;
-    LOG(ERROR) << buf;
-}
-
 int next_token(struct parse_state *state)
 {
     char *x = state->ptr;
diff --git a/init/parser.h b/init/parser.h
index 95e1164..3dcc566 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,14 +27,8 @@
     char *text;
     int line;
     int nexttoken;
-    void *context;
-    void (*parse_line)(struct parse_state *state, int nargs, char **args);
-    const char *filename;
-    void *priv;
 };
 
-void dump_parser_state(void);
 int next_token(struct parse_state *state);
-void parse_error(struct parse_state *state, const char *fmt, ...);
 
 #endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 20a2aa1..aa47976 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -43,6 +43,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <bootimg.h>
@@ -573,10 +574,28 @@
     }
 }
 
+// persist.sys.usb.config values can't be combined on build-time when property
+// files are split into each partition.
+// So we need to apply the same rule of build/make/tools/post_process_props.py
+// on runtime.
+static void update_sys_usb_config() {
+    bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+    std::string config = android::base::GetProperty("persist.sys.usb.config", "");
+    if (config.empty()) {
+        property_set("persist.sys.usb.config", is_debuggable ? "adb" : "none");
+    } else if (is_debuggable && config.find("adb") == std::string::npos &&
+               config.length() + 4 < PROP_VALUE_MAX) {
+        config.append(",adb");
+        property_set("persist.sys.usb.config", config);
+    }
+}
+
 void property_load_boot_defaults() {
     load_properties_from_file("/default.prop", NULL);
     load_properties_from_file("/odm/default.prop", NULL);
     load_properties_from_file("/vendor/default.prop", NULL);
+
+    update_sys_usb_config();
 }
 
 static void load_override_properties() {
diff --git a/init/service.cpp b/init/service.cpp
index c0745e3..961926a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -534,14 +534,14 @@
 }
 
 class Service::OptionParserMap : public KeywordMap<OptionParser> {
-public:
-    OptionParserMap() {
-    }
-private:
-    Map& map() const override;
+  public:
+    OptionParserMap() {}
+
+  private:
+    const Map& map() const override;
 };
 
-Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map option_parsers = {
@@ -572,13 +572,8 @@
 }
 
 bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
-    if (args.empty()) {
-        *err = "option needed, but not provided";
-        return false;
-    }
-
     static const OptionParserMap parser_map;
-    auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
+    auto parser = parser_map.FindFunction(args, err);
 
     if (!parser) {
         return false;
@@ -877,11 +872,6 @@
 }
 
 void ServiceManager::AddService(std::unique_ptr<Service> service) {
-    Service* old_service = FindServiceByName(service->name());
-    if (old_service) {
-        LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
-        return;
-    }
     services_.emplace_back(std::move(service));
 }
 
@@ -1095,7 +1085,7 @@
     }
 }
 
-bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line, std::string* err) {
     if (args.size() < 3) {
         *err = "services must have a name and a program";
@@ -1108,19 +1098,24 @@
         return false;
     }
 
+    Service* old_service = service_manager_->FindServiceByName(name);
+    if (old_service) {
+        *err = "ignored duplicate definition of service '" + name + "'";
+        return false;
+    }
+
     std::vector<std::string> str_args(args.begin() + 2, args.end());
     service_ = std::make_unique<Service>(name, str_args);
     return true;
 }
 
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, int line,
-                                     std::string* err) {
-    return service_ ? service_->ParseLine(args, err) : false;
+bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    return service_ ? service_->ParseLine(std::move(args), err) : false;
 }
 
 void ServiceParser::EndSection() {
     if (service_) {
-        ServiceManager::GetInstance().AddService(std::move(service_));
+        service_manager_->AddService(std::move(service_));
     }
 }
 
diff --git a/init/service.h b/init/service.h
index 6fe0258..634fe4e 100644
--- a/init/service.h
+++ b/init/service.h
@@ -213,16 +213,17 @@
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser() : service_(nullptr) {}
-    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+    ServiceParser(ServiceManager* service_manager)
+        : service_manager_(service_manager), service_(nullptr) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
     void EndSection() override;
-    void EndFile(const std::string&) override {}
 
   private:
     bool IsValidName(const std::string& name) const;
 
+    ServiceManager* service_manager_;
     std::unique_ptr<Service> service_;
 };
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index b6c6a01..963cc4d 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -18,9 +18,7 @@
 
 #include <ctype.h>
 #include <fcntl.h>
-#include <grp.h>
 #include <poll.h>
-#include <pwd.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,9 +31,11 @@
 
 #include "devices.h"
 #include "log.h"
-#include "ueventd_parser.h"
 #include "util.h"
 
+template <bool sysfs>
+static bool ParseSingleLine(std::vector<std::string>&& line, std::string* err);
+
 int ueventd_main(int argc, char **argv)
 {
     /*
@@ -60,9 +60,14 @@
     cb.func_log = selinux_klog_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
-    ueventd_parse_config_file("/ueventd.rc");
-    ueventd_parse_config_file("/vendor/ueventd.rc");
-    ueventd_parse_config_file("/odm/ueventd.rc");
+    Parser& parser = Parser::GetInstance();
+    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>());
+    using namespace std::placeholders;
+    parser.AddSingleLineParser("/sys/", std::bind(ParsePermissionsLine, _1, _2, true));
+    parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, _2, false));
+    parser.ParseConfig("/ueventd.rc");
+    parser.ParseConfig("/vendor/ueventd.rc");
+    parser.ParseConfig("/odm/ueventd.rc");
 
     /*
      * keep the current product name base configuration so
@@ -72,7 +77,7 @@
      * device node entries (b/34968103)
      */
     std::string hardware = android::base::GetProperty("ro.hardware", "");
-    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
+    parser.ParseConfig("/ueventd." + hardware + ".rc");
 
     device_init();
 
@@ -93,59 +98,3 @@
 
     return 0;
 }
-
-void set_device_permission(const char* fn, int line, int nargs, char **args)
-{
-    char *name;
-    char *attr = 0;
-    mode_t perm;
-    uid_t uid;
-    gid_t gid;
-    char *endptr;
-
-    if (nargs == 0)
-        return;
-
-    if (args[0][0] == '#')
-        return;
-
-    name = args[0];
-
-    if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
-        LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1];
-        attr = args[1];
-        args++;
-        nargs--;
-    }
-
-    if (nargs != 4) {
-        LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'";
-        return;
-    }
-
-    perm = strtol(args[1], &endptr, 8);
-    if (!endptr || *endptr != '\0') {
-        LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
-        return;
-    }
-
-    struct passwd* pwd = getpwnam(args[2]);
-    if (!pwd) {
-        LOG(ERROR) << "invalid uid (" << fn << ":" << line << ") '" << args[2] << "'";
-        return;
-    }
-    uid = pwd->pw_uid;
-
-    struct group* grp = getgrnam(args[3]);
-    if (!grp) {
-        LOG(ERROR) << "invalid gid (" << fn << ":" << line << ") '" << args[3] << "'";
-        return;
-    }
-    gid = grp->gr_gid;
-
-    if (attr) {
-        sysfs_permissions.emplace_back(name, attr, perm, uid, gid);
-    } else {
-        dev_permissions.emplace_back(name, perm, uid, gid);
-    }
-}
diff --git a/init/ueventd.h b/init/ueventd.h
index d44d1ca..1f424d3 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,24 +17,6 @@
 #ifndef _INIT_UEVENTD_H_
 #define _INIT_UEVENTD_H_
 
-#include <sys/types.h>
-
-#include <cutils/list.h>
-
-enum devname_src_t {
-    DEVNAME_UNKNOWN = 0,
-    DEVNAME_UEVENT_DEVNAME,
-    DEVNAME_UEVENT_DEVPATH,
-};
-
-struct ueventd_subsystem {
-    struct listnode slist;
-
-    const char *name;
-    const char *dirname;
-    devname_src_t devname_src;
-};
-
-int ueventd_main(int argc, char **argv);
+int ueventd_main(int argc, char** argv);
 
 #endif
diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h
deleted file mode 100644
index 88e8f01..0000000
--- a/init/ueventd_keywords.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef KEYWORD
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs) K_##symbol,
-enum {
-    K_UNKNOWN,
-#endif
-    KEYWORD(subsystem,      SECTION,    1)
-    KEYWORD(devname,        OPTION,     1)
-    KEYWORD(dirname,        OPTION,     1)
-#ifdef __MAKE_KEYWORD_ENUM__
-    KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
deleted file mode 100644
index 510d7bb..0000000
--- a/init/ueventd_parser.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2010 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 "ueventd_parser.h"
-
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-
-#include "parser.h"
-#include "util.h"
-
-static list_declare(subsystem_list);
-
-static void parse_line_device(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define OPTION  0x02
-
-#include "ueventd_keywords.h"
-
-#define KEYWORD(symbol, flags, nargs) \
-    [ K_##symbol ] = { #symbol, (nargs) + 1, flags, },
-
-static struct {
-    const char *name;
-    unsigned char nargs;
-    unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
-    [ K_UNKNOWN ] = { "unknown", 0, 0 },
-#include "ueventd_keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-static int lookup_keyword(const char *s)
-{
-    switch (*s++) {
-    case 'd':
-        if (!strcmp(s, "evname")) return K_devname;
-        if (!strcmp(s, "irname")) return K_dirname;
-        break;
-    case 's':
-        if (!strcmp(s, "ubsystem")) return K_subsystem;
-        break;
-    }
-    return K_UNKNOWN;
-}
-
-static void parse_line_no_op(struct parse_state*, int, char**) {
-}
-
-static int valid_name(const char *name)
-{
-    while (*name) {
-        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
-            return 0;
-        }
-        name++;
-    }
-    return 1;
-}
-
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
-{
-    struct listnode *node;
-    struct ueventd_subsystem *s;
-
-    list_for_each(node, &subsystem_list) {
-        s = node_to_item(node, struct ueventd_subsystem, slist);
-        if (!strcmp(s->name, name)) {
-            return s;
-        }
-    }
-    return 0;
-}
-
-static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
-    if (!valid_name(args[1])) {
-        parse_error(state, "invalid subsystem name '%s'\n", args[1]);
-        return 0;
-    }
-
-    ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
-    if (s) {
-        parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
-                args[1]);
-        return 0;
-    }
-
-    s = (ueventd_subsystem*) calloc(1, sizeof(*s));
-    if (!s) {
-        parse_error(state, "out of memory\n");
-        return 0;
-    }
-    s->name = args[1];
-    s->dirname = "/dev";
-    list_add_tail(&subsystem_list, &s->slist);
-    return s;
-}
-
-static void parse_line_subsystem(struct parse_state *state, int nargs,
-        char **args)
-{
-    struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
-    int kw;
-
-    if (nargs == 0) {
-        return;
-    }
-
-    kw = lookup_keyword(args[0]);
-    switch (kw) {
-    case K_devname:
-        if (!strcmp(args[1], "uevent_devname"))
-            s->devname_src = DEVNAME_UEVENT_DEVNAME;
-        else if (!strcmp(args[1], "uevent_devpath"))
-            s->devname_src = DEVNAME_UEVENT_DEVPATH;
-        else
-            parse_error(state, "invalid devname '%s'\n", args[1]);
-        break;
-
-    case K_dirname:
-        if (args[1][0] == '/')
-            s->dirname = args[1];
-        else
-            parse_error(state, "dirname '%s' does not start with '/'\n",
-                    args[1]);
-        break;
-
-    default:
-        parse_error(state, "invalid option '%s'\n", args[0]);
-    }
-}
-
-static void parse_new_section(struct parse_state *state, int kw,
-                       int nargs, char **args)
-{
-    printf("[ %s %s ]\n", args[0],
-           nargs > 1 ? args[1] : "");
-
-    switch(kw) {
-    case K_subsystem:
-        state->context = parse_subsystem(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_subsystem;
-            return;
-        }
-        break;
-    }
-    state->parse_line = parse_line_no_op;
-}
-
-static void parse_line(struct parse_state *state, char **args, int nargs)
-{
-    int kw = lookup_keyword(args[0]);
-    int kw_nargs = kw_nargs(kw);
-
-    if (nargs < kw_nargs) {
-        parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
-            kw_nargs > 2 ? "arguments" : "argument");
-        return;
-    }
-
-    if (kw_is(kw, SECTION)) {
-        parse_new_section(state, kw, nargs, args);
-    } else if (kw_is(kw, OPTION)) {
-        state->parse_line(state, nargs, args);
-    } else {
-        parse_line_device(state, nargs, args);
-    }
-}
-
-static void parse_config(const char *fn, const std::string& data)
-{
-    char *args[UEVENTD_PARSER_MAXARGS];
-
-    int nargs = 0;
-    parse_state state;
-    state.filename = fn;
-    state.line = 1;
-    state.ptr = strdup(data.c_str());  // TODO: fix this code!
-    state.nexttoken = 0;
-    state.parse_line = parse_line_no_op;
-    for (;;) {
-        int token = next_token(&state);
-        switch (token) {
-        case T_EOF:
-            parse_line(&state, args, nargs);
-            return;
-        case T_NEWLINE:
-            if (nargs) {
-                parse_line(&state, args, nargs);
-                nargs = 0;
-            }
-            state.line++;
-            break;
-        case T_TEXT:
-            if (nargs < UEVENTD_PARSER_MAXARGS) {
-                args[nargs++] = state.text;
-            }
-            break;
-        }
-    }
-}
-
-int ueventd_parse_config_file(const char *fn)
-{
-    std::string data;
-    if (!read_file(fn, &data)) {
-        return -1;
-    }
-
-    data.push_back('\n'); // TODO: fix parse_config.
-    parse_config(fn, data);
-    return 0;
-}
-
-static void parse_line_device(parse_state* state, int nargs, char** args) {
-    set_device_permission(state->filename, state->line, nargs, args);
-}
diff --git a/init/util.cpp b/init/util.cpp
index 32ae93d..a101ce5 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -369,3 +369,26 @@
     os << t.duration_s() << " seconds";
     return os;
 }
+
+// Reads the content of device tree file under kAndroidDtDir directory.
+// Returns true if the read is success, false otherwise.
+bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
+    const std::string file_name = kAndroidDtDir + sub_path;
+    if (android::base::ReadFileToString(file_name, dt_content)) {
+        if (!dt_content->empty()) {
+            dt_content->pop_back();  // Trims the trailing '\0' out.
+            return true;
+        }
+    }
+    return false;
+}
+
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
+    std::string dt_content;
+    if (read_android_dt_file(sub_path, &dt_content)) {
+        if (dt_content == expected_content) {
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/init/util.h b/init/util.h
index 0bb9cdf..92b3a1d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -29,6 +29,8 @@
 
 #define COLDBOOT_DONE "/dev/.coldboot_done"
 
+const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
+
 using android::base::boot_clock;
 using namespace std::chrono_literals;
 
@@ -72,4 +74,8 @@
 
 void panic() __attribute__((__noreturn__));
 
+// Reads or compares the content of device tree file under kAndroidDtDir directory.
+bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
+
 #endif
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
new file mode 100644
index 0000000..296bd26
--- /dev/null
+++ b/libsysutils/Android.bp
@@ -0,0 +1,25 @@
+cc_library_shared {
+    name: "libsysutils",
+    srcs: [
+        "src/SocketListener.cpp",
+        "src/FrameworkListener.cpp",
+        "src/NetlinkListener.cpp",
+        "src/NetlinkEvent.cpp",
+        "src/FrameworkCommand.cpp",
+        "src/SocketClient.cpp",
+        "src/ServiceManager.cpp",
+    ],
+
+    logtags: ["EventLogTags.logtags"],
+
+    cflags: ["-Werror"],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libnl",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
deleted file mode 100644
index 584e5a2..0000000
--- a/libsysutils/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=                             \
-                  src/SocketListener.cpp      \
-                  src/FrameworkListener.cpp   \
-                  src/NetlinkListener.cpp     \
-                  src/NetlinkEvent.cpp        \
-                  src/FrameworkCommand.cpp    \
-                  src/SocketClient.cpp        \
-                  src/ServiceManager.cpp      \
-                  EventLogTags.logtags
-
-LOCAL_MODULE:= libsysutils
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-        libbase \
-        libcutils \
-        liblog \
-        libnl
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 594bd0d..0b6b28c 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -32,7 +32,6 @@
 static const uint64_t monthSec = 31 * 24 * hourSec;
 
 size_t LogStatistics::SizesTotal;
-size_t TagNameEntry::droppedElsewhere;
 
 LogStatistics::LogStatistics() : enable(false) {
     log_time now(CLOCK_REALTIME);
@@ -153,8 +152,6 @@
             tagTable.add(tag, element);
         }
     }
-
-    tagNameTable.add(TagNameEntry(element).getKey(), element);
 }
 
 void LogStatistics::subtract(LogBufferElement* element) {
@@ -194,8 +191,6 @@
             tagTable.subtract(tag, element);
         }
     }
-
-    tagNameTable.subtract(TagNameEntry(element).getKey(), element);
 }
 
 // Atomically set an entry to drop
@@ -230,8 +225,6 @@
             tagTable.drop(tag, element);
         }
     }
-
-    tagNameTable.drop(TagNameEntry(element).getKey(), element);
 }
 
 // caller must own and free character string
@@ -512,71 +505,6 @@
     return formatLine(name, size, pruned);
 }
 
-std::string TagNameEntry::formatHeader(const std::string& name,
-                                       log_id_t /* id */) const {
-    return formatLine(name, std::string("Size"), std::string("Prune")) +
-           formatLine(std::string("  TID/PID/UID   LOG_TAG NAME"),
-                      std::string("BYTES"), std::string("NUM"));
-}
-
-std::string TagNameEntry::format(const LogStatistics& /* stat */,
-                                 log_id_t /* id */) const {
-    std::string name;
-    pid_t tid = getTid();
-    pid_t pid = getPid();
-    std::string pidstr;
-    if (pid != (pid_t)-1) {
-        pidstr = android::base::StringPrintf("%u", pid);
-        if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr;
-    }
-    int len = 9 - pidstr.length();
-    if (len < 0) len = 0;
-    if ((tid == (pid_t)-1) || (tid == pid)) {
-        name = android::base::StringPrintf("%*s", len, "");
-    } else {
-        name = android::base::StringPrintf("%*u", len, tid);
-    }
-    name += pidstr;
-    uid_t uid = getUid();
-    if (uid != (uid_t)-1) {
-        name += android::base::StringPrintf("/%u", uid);
-    }
-
-    std::string size = android::base::StringPrintf("%zu", getSizes());
-
-    const char* nameTmp = getName();
-    if (nameTmp) {
-        size_t lenSpace = std::max(16 - name.length(), (size_t)1);
-        size_t len = EntryBaseConstants::total_len -
-                     EntryBaseConstants::pruned_len - size.length() -
-                     name.length() - lenSpace - 2;
-        size_t lenNameTmp = strlen(nameTmp);
-        while ((len < lenNameTmp) && (lenSpace > 1)) {
-            ++len;
-            --lenSpace;
-        }
-        name += android::base::StringPrintf("%*s", (int)lenSpace, "");
-        if (len < lenNameTmp) {
-            name += "...";
-            nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
-        }
-        name += nameTmp;
-    }
-
-    std::string pruned = "";
-
-    // Because of TagNameEntry::droppedElsewhere, dropped count can get
-    // modified for "chatty" tags.  Since the size is always zero this
-    // modification is a "can not happen".
-    TagNameEntry& me = const_cast<TagNameEntry&>(*this);
-    size_t dropped = me.getDropped();
-    if (dropped) {
-        pruned = android::base::StringPrintf("%zu", dropped);
-    }
-
-    return formatLine(name, size, pruned);
-}
-
 static std::string formatMsec(uint64_t val) {
     static const unsigned subsecDigits = 3;
     static const uint64_t sec = MS_PER_SEC;
@@ -797,13 +725,6 @@
         output += tidTable.format(*this, uid, pid, name);
     }
 
-    if (enable) {
-        name = "Chattiest TAGs";
-        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
-        name += ":";
-        output += tagNameTable.format(*this, uid, pid, name);
-    }
-
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
         name = "Chattiest events log buffer TAGs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 84dbff3..e6f01d6 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -18,14 +18,10 @@
 #define _LOGD_LOG_STATISTICS_H__
 
 #include <ctype.h>
-#include <inttypes.h>
-#include <stdint.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/types.h>
 
 #include <algorithm>  // std::max
-#include <experimental/string_view>
 #include <memory>
 #include <string>  // std::string
 #include <unordered_map>
@@ -81,7 +77,7 @@
     std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
                                            size_t len) const {
         if (!len) {
-            std::unique_ptr<const TEntry* []> sorted(nullptr);
+            std::unique_ptr<const TEntry* []> sorted(NULL);
             return sorted;
         }
 
@@ -322,7 +318,7 @@
         : EntryBaseDropped(element),
           pid(element.pid),
           uid(element.uid),
-          name(element.name ? strdup(element.name) : nullptr) {
+          name(element.name ? strdup(element.name) : NULL) {
     }
     ~PidEntry() {
         free(name);
@@ -344,7 +340,7 @@
     inline void add(pid_t newPid) {
         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
-            name = nullptr;
+            name = NULL;
         }
         if (!name) {
             name = android::pidToName(newPid);
@@ -392,7 +388,7 @@
           tid(element.tid),
           pid(element.pid),
           uid(element.uid),
-          name(element.name ? strdup(element.name) : nullptr) {
+          name(element.name ? strdup(element.name) : NULL) {
     }
     ~TidEntry() {
         free(name);
@@ -417,7 +413,7 @@
     inline void add(pid_t incomingTid) {
         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
-            name = nullptr;
+            name = NULL;
         }
         if (!name) {
             name = android::tidToName(incomingTid);
@@ -481,141 +477,6 @@
     std::string format(const LogStatistics& stat, log_id_t id) const;
 };
 
-struct TagNameEntry : public EntryBaseDropped {
-    // We do not care that a dropped entry does, or does not exist, because
-    // the size will always report zero.  But if it does, we need to account
-    // for the ones that transitioned from a known tag to chatty so that we
-    // do not spring a leak on the dropped counts.
-    static size_t droppedElsewhere;
-
-    pid_t tid;
-    pid_t pid;
-    uid_t uid;
-    std::string* alloc;
-    std::experimental::string_view name;  // Saves space if const char*
-
-    explicit TagNameEntry(LogBufferElement* element)
-        : EntryBaseDropped(element),
-          tid(element->getTid()),
-          pid(element->getPid()),
-          uid(element->getUid()),
-          alloc(nullptr) {
-        if (element->isBinary()) {
-            uint32_t tag = element->getTag();
-            if (tag) {
-                const char* cp = android::tagToName(tag);
-                if (cp) {
-                    name = std::experimental::string_view(cp, strlen(cp));
-                    return;
-                }
-            }
-            alloc = new std::string(
-                android::base::StringPrintf("[%" PRIu32 "]", tag));
-            if (!alloc) return;
-            name = std::experimental::string_view(alloc->c_str(), alloc->size());
-            return;
-        }
-        const char* msg = element->getMsg();
-        if (!msg) {
-            name = std::experimental::string_view("chatty", strlen("chatty"));
-            return;
-        }
-        ++msg;
-        unsigned short len = element->getMsgLen();
-        len = (len <= 1) ? 0 : strnlen(msg, len - 1);
-        if (!len) {
-            name = std::experimental::string_view("<NULL>", strlen("<NULL>"));
-            return;
-        }
-        alloc = new std::string(msg, len);
-        if (!alloc) return;
-        name = std::experimental::string_view(alloc->c_str(), alloc->size());
-    }
-
-    explicit TagNameEntry(TagNameEntry&& rval)
-        : tid(rval.tid),
-          pid(rval.pid),
-          uid(rval.uid),
-          alloc(rval.alloc),
-          name(rval.name.data(), rval.name.length()) {
-        rval.alloc = nullptr;
-    }
-
-    explicit TagNameEntry(const TagNameEntry& rval)
-        : tid(rval.tid),
-          pid(rval.pid),
-          uid(rval.uid),
-          alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
-          name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
-    }
-
-    ~TagNameEntry() {
-        if (alloc) delete alloc;
-    }
-
-    const std::experimental::string_view& getKey() const {
-        return name;
-    }
-    const pid_t& getTid() const {
-        return tid;
-    }
-    const pid_t& getPid() const {
-        return pid;
-    }
-    const uid_t& getUid() const {
-        return uid;
-    }
-    const char* getName() const {
-        return name.data();
-    }
-    size_t getNameAllocLength() const {
-        return alloc ? alloc->length() + 1 : 0;
-    }
-
-    size_t getDropped() {
-        if (__predict_false(name == "chatty")) {  // plug the leak
-            dropped += droppedElsewhere;
-            droppedElsewhere = 0;
-        }
-        return EntryBaseDropped::getDropped();
-    }
-
-    inline void add(LogBufferElement* element) {
-        if (uid != element->getUid()) {
-            uid = -1;
-        }
-        if (pid != element->getPid()) {
-            pid = -1;
-        }
-        if (tid != element->getTid()) {
-            tid = -1;
-        }
-        // Fixup of dropped can be deferred.
-        EntryBaseDropped::add(element);
-    }
-
-    inline bool subtract(LogBufferElement* element) {
-        if (__predict_false(name == "chatty")) {  // plug the leak
-            dropped += droppedElsewhere;
-            droppedElsewhere = 0;
-        }
-        return EntryBaseDropped::subtract(element);
-    }
-
-    inline void drop(LogBufferElement* element) {
-        if (__predict_false(name == "chatty")) {  // plug the leak
-            dropped += droppedElsewhere;
-            droppedElsewhere = 0;
-        } else {
-            ++droppedElsewhere;
-        }
-        return EntryBaseDropped::drop(element);
-    }
-
-    std::string formatHeader(const std::string& name, log_id_t id) const;
-    std::string format(const LogStatistics& stat, log_id_t id) const;
-};
-
 template <typename TEntry>
 class LogFindWorst {
     std::unique_ptr<const TEntry* []> sorted;
@@ -689,15 +550,9 @@
     // security tag list
     tagTable_t securityTagTable;
 
-    // global tag list
-    typedef LogHashtable<std::experimental::string_view, TagNameEntry>
-        tagNameTable_t;
-    tagNameTable_t tagNameTable;
-
     size_t sizeOf() const {
         size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
                       tagTable.sizeOf() + securityTagTable.sizeOf() +
-                      tagNameTable.sizeOf() +
                       (pidTable.size() * sizeof(pidTable_t::iterator)) +
                       (tagTable.size() * sizeof(tagTable_t::iterator));
         for (auto it : pidTable) {
@@ -708,7 +563,6 @@
             const char* name = it.second.getName();
             if (name) size += strlen(name) + 1;
         }
-        for (auto it : tagNameTable) size += it.second.getNameAllocLength();
         log_id_for_each(id) {
             size += uidTable[id].sizeOf();
             size += uidTable[id].size() * sizeof(uidTable_t::iterator);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index e51a3e3..2dd5771 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -495,13 +495,28 @@
     # Set indication (checked by vold) that we have finished this action
     #setprop vold.post_fs_data_done 1
 
-# This trigger will be triggered before 'zygote-start' since there is no zygote-start defined in
-# current init.rc. It is recommended to put unnecessary data/ initialization from post-fs-data
-# to start-zygote to unblock zygote start.
+# It is recommended to put unnecessary data/ initialization from post-fs-data
+# to start-zygote in device's init.rc to unblock zygote start.
+on zygote-start && property:ro.crypto.state=unencrypted
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier_nonencrypted
+    start netd
+    start zygote
+    start zygote_secondary
+
+on zygote-start && property:ro.crypto.state=unsupported
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier_nonencrypted
+    start netd
+    start zygote
+    start zygote_secondary
+
 on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
-   start netd
-   start zygote
-   start zygote_secondary
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier_nonencrypted
+    start netd
+    start zygote
+    start zygote_secondary
 
 on boot
     # basic network init
@@ -600,8 +615,6 @@
     class_start core
 
 on nonencrypted
-    # A/B update verifier that marks a successful boot.
-    exec_start update_verifier_nonencrypted
     class_start main
     class_start late_start