diff --git a/init/action.h b/init/action.h index 1534bf987..eddc384cd 100644 --- a/init/action.h +++ b/init/action.h @@ -22,6 +22,8 @@ #include #include +#include + #include "builtins.h" #include "keyword_map.h" #include "result.h" @@ -79,6 +81,7 @@ class Action { static void set_function_map(const BuiltinFunctionMap* function_map) { function_map_ = function_map; } + bool IsFromApex() const { return base::StartsWith(filename_, "/apex/"); } private: void ExecuteCommand(const Command& command) const; diff --git a/init/action_manager.h b/init/action_manager.h index b6f93d9b5..2746a7c4a 100644 --- a/init/action_manager.h +++ b/init/action_manager.h @@ -37,6 +37,10 @@ class ActionManager { size_t CheckAllCommands(); void AddAction(std::unique_ptr action); + template + void RemoveActionIf(UnaryPredicate predicate) { + actions_.erase(std::remove_if(actions_.begin(), actions_.end(), predicate), actions_.end()); + } void QueueEventTrigger(const std::string& trigger); void QueuePropertyChange(const std::string& name, const std::string& value); void QueueAllPropertyActions(); diff --git a/init/builtins.cpp b/init/builtins.cpp index 01db4f5da..9e1d93c6b 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -1288,7 +1288,8 @@ static Result parse_apex_configs() { return Error() << "glob pattern '" << glob_pattern << "' failed"; } std::vector configs; - Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance(), true); + Parser parser = + CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance()); for (size_t i = 0; i < glob_result.gl_pathc; i++) { std::string path = glob_result.gl_pathv[i]; // Filter-out /apex/@ paths. The paths are bind-mounted to diff --git a/init/init.cpp b/init/init.cpp index f8330bc5b..fd8ee0fe8 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -293,13 +293,15 @@ Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) { return parser; } -// parser that only accepts new services -Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) { +// Returns a Parser that accepts scripts from APEX modules. It supports `service` and `on`. +Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list) { Parser parser; parser.AddSectionParser( "service", std::make_unique(&service_list, GetSubcontext(), std::nullopt, - from_apex)); + /*from_apex=*/true)); + parser.AddSectionParser("on", std::make_unique(&action_manager, GetSubcontext())); + return parser; } diff --git a/init/init.h b/init/init.h index 4f686cb5b..522053549 100644 --- a/init/init.h +++ b/init/init.h @@ -29,7 +29,7 @@ namespace android { namespace init { Parser CreateParser(ActionManager& action_manager, ServiceList& service_list); -Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex); +Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list); bool start_waiting_for_property(const char *name, const char *value); diff --git a/init/init_test.cpp b/init/init_test.cpp index 8c19d5f5a..0dc6ff640 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -42,34 +42,34 @@ namespace init { using ActionManagerCommand = std::function; void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map, - const std::vector& commands, ServiceList* service_list) { - ActionManager am; - + const std::vector& commands, ActionManager* action_manager, + ServiceList* service_list) { Action::set_function_map(&test_function_map); Parser parser; parser.AddSectionParser("service", std::make_unique(service_list, nullptr, std::nullopt)); - parser.AddSectionParser("on", std::make_unique(&am, nullptr)); + parser.AddSectionParser("on", std::make_unique(action_manager, nullptr)); parser.AddSectionParser("import", std::make_unique(&parser)); ASSERT_TRUE(parser.ParseConfig(init_script_file)); for (const auto& command : commands) { - command(am); + command(*action_manager); } - while (am.HasMoreCommands()) { - am.ExecuteOneCommand(); + while (action_manager->HasMoreCommands()) { + action_manager->ExecuteOneCommand(); } } void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map, - const std::vector& commands, ServiceList* service_list) { + const std::vector& commands, ActionManager* action_manager, + ServiceList* service_list) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd)); - TestInit(tf.path, test_function_map, commands, service_list); + TestInit(tf.path, test_function_map, commands, action_manager, service_list); } TEST(init, SimpleEventTrigger) { @@ -91,8 +91,9 @@ pass_test ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; std::vector commands{trigger_boot}; + ActionManager action_manager; ServiceList service_list; - TestInitText(init_script, test_function_map, commands, &service_list); + TestInitText(init_script, test_function_map, commands, &action_manager, &service_list); EXPECT_TRUE(expect_true); } @@ -154,8 +155,9 @@ execute_third ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; std::vector commands{trigger_boot}; + ActionManager action_manager; ServiceList service_list; - TestInitText(init_script, test_function_map, commands, &service_list); + TestInitText(init_script, test_function_map, commands, &action_manager, &service_list); EXPECT_EQ(3, num_executed); } @@ -170,8 +172,9 @@ service A something )init"; + ActionManager action_manager; ServiceList service_list; - TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list); + TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list); ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end())); auto service = service_list.begin()->get(); @@ -237,13 +240,100 @@ TEST(init, EventTriggerOrderMultipleFiles) { ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; std::vector commands{trigger_boot}; + ActionManager action_manager; ServiceList service_list; - - TestInit(start.path, test_function_map, commands, &service_list); + TestInit(start.path, test_function_map, commands, &action_manager, &service_list); EXPECT_EQ(6, num_executed); } +BuiltinFunctionMap GetTestFunctionMapForLazyLoad(int& num_executed, ActionManager& action_manager) { + auto execute_command = [&num_executed](const BuiltinArguments& args) { + EXPECT_EQ(2U, args.size()); + EXPECT_EQ(++num_executed, std::stoi(args[1])); + return Result{}; + }; + auto load_command = [&action_manager](const BuiltinArguments& args) -> Result { + EXPECT_EQ(2U, args.size()); + Parser parser; + parser.AddSectionParser("on", std::make_unique(&action_manager, nullptr)); + if (!parser.ParseConfig(args[1])) { + return Error() << "Failed to load"; + } + return Result{}; + }; + auto trigger_command = [&action_manager](const BuiltinArguments& args) { + EXPECT_EQ(2U, args.size()); + LOG(INFO) << "Queue event trigger: " << args[1]; + action_manager.QueueEventTrigger(args[1]); + return Result{}; + }; + BuiltinFunctionMap test_function_map = { + {"execute", {1, 1, {false, execute_command}}}, + {"load", {1, 1, {false, load_command}}}, + {"trigger", {1, 1, {false, trigger_command}}}, + }; + return test_function_map; +} + +TEST(init, LazilyLoadedActionsCantBeTriggeredByTheSameTrigger) { + // "start" script loads "lazy" script. Even though "lazy" scripts + // defines "on boot" action, it's not executed by the current "boot" + // event because it's already processed. + TemporaryFile lazy; + ASSERT_TRUE(lazy.fd != -1); + ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", lazy.fd)); + + TemporaryFile start; + // clang-format off + std::string start_script = "on boot\n" + "load " + std::string(lazy.path) + "\n" + "execute 1"; + // clang-format on + ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd)); + + int num_executed = 0; + ActionManager action_manager; + ServiceList service_list; + BuiltinFunctionMap test_function_map = + GetTestFunctionMapForLazyLoad(num_executed, action_manager); + + ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; + std::vector commands{trigger_boot}; + TestInit(start.path, test_function_map, commands, &action_manager, &service_list); + + EXPECT_EQ(1, num_executed); +} + +TEST(init, LazilyLoadedActionsCanBeTriggeredByTheNextTrigger) { + // "start" script loads "lazy" script and then triggers "next" event + // which executes "on next" action loaded by the previous command. + TemporaryFile lazy; + ASSERT_TRUE(lazy.fd != -1); + ASSERT_TRUE(android::base::WriteStringToFd("on next\nexecute 2", lazy.fd)); + + TemporaryFile start; + // clang-format off + std::string start_script = "on boot\n" + "load " + std::string(lazy.path) + "\n" + "execute 1\n" + "trigger next"; + // clang-format on + ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd)); + + int num_executed = 0; + ActionManager action_manager; + ServiceList service_list; + BuiltinFunctionMap test_function_map = + GetTestFunctionMapForLazyLoad(num_executed, action_manager); + + ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; + std::vector commands{trigger_boot}; + TestInit(start.path, test_function_map, commands, &action_manager, &service_list); + + EXPECT_EQ(2, num_executed); +} + TEST(init, RejectsCriticalAndOneshotService) { if (GetIntProperty("ro.product.first_api_level", 10000) < 30) { GTEST_SKIP() << "Test only valid for devices launching with R or later"; diff --git a/init/reboot.cpp b/init/reboot.cpp index 41cf748d8..4e4bfd875 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -892,7 +892,16 @@ static Result DoUserspaceReboot() { sub_reason = "ns_switch"; return Error() << "Failed to switch to bootstrap namespace"; } - // Remove services that were defined in an APEX. + ActionManager::GetInstance().RemoveActionIf([](const auto& action) -> bool { + if (action->IsFromApex()) { + std::string trigger_name = action->BuildTriggersString(); + LOG(INFO) << "Removing action (" << trigger_name << ") from (" << action->filename() + << ":" << action->line() << ")"; + return true; + } + return false; + }); + // Remove services that were defined in an APEX ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr& s) -> bool { if (s->is_from_apex()) { LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";