APEX configs support 'on' as well
APEX configs have supported only 'service' definitions. For those services relying on 'on' trigger actions, we had to have separate config files installed in read-only partitions (e.g. /system/etc/init). This was suboptimal because even though APEXes are updatable, read-only partitions are not. Now, 'on' is supported in APEX configs. Putting 'on' trigger actions near to service definitions makes APEX more self-contained. 'on' trigger actions loaded from APEX configs are not sticky. So, events happens before loading APEX configs can't trigger actions. For example, 'post-fs-data' is where APEX configs are loaded for now, so 'on post-fs-data' in APEX configs can't be triggerd. Bug: 202731768 Test: atest CtsInitTestCases Change-Id: I5a01d9c7c57b07955b829d6cc157e7f0c91166f9
This commit is contained in:
parent
dbe14f2f73
commit
badb7de1a2
7 changed files with 129 additions and 20 deletions
|
@ -22,6 +22,8 @@
|
|||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -37,6 +37,10 @@ class ActionManager {
|
|||
size_t CheckAllCommands();
|
||||
|
||||
void AddAction(std::unique_ptr<Action> action);
|
||||
template <class UnaryPredicate>
|
||||
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();
|
||||
|
|
|
@ -1288,7 +1288,8 @@ static Result<void> parse_apex_configs() {
|
|||
return Error() << "glob pattern '" << glob_pattern << "' failed";
|
||||
}
|
||||
std::vector<std::string> 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/<name>@<ver> paths. The paths are bind-mounted to
|
||||
|
|
|
@ -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<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
|
||||
from_apex));
|
||||
/*from_apex=*/true));
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -42,34 +42,34 @@ namespace init {
|
|||
using ActionManagerCommand = std::function<void(ActionManager&)>;
|
||||
|
||||
void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
|
||||
const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
|
||||
ActionManager am;
|
||||
|
||||
const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
|
||||
ServiceList* service_list) {
|
||||
Action::set_function_map(&test_function_map);
|
||||
|
||||
Parser parser;
|
||||
parser.AddSectionParser("service",
|
||||
std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(action_manager, nullptr));
|
||||
parser.AddSectionParser("import", std::make_unique<ImportParser>(&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<ActionManagerCommand>& commands, ServiceList* service_list) {
|
||||
const std::vector<ActionManagerCommand>& 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<ActionManagerCommand> 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<ActionManagerCommand> 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<ActionManagerCommand> 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<void>{};
|
||||
};
|
||||
auto load_command = [&action_manager](const BuiltinArguments& args) -> Result<void> {
|
||||
EXPECT_EQ(2U, args.size());
|
||||
Parser parser;
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, nullptr));
|
||||
if (!parser.ParseConfig(args[1])) {
|
||||
return Error() << "Failed to load";
|
||||
}
|
||||
return Result<void>{};
|
||||
};
|
||||
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<void>{};
|
||||
};
|
||||
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<ActionManagerCommand> 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<ActionManagerCommand> 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";
|
||||
|
|
|
@ -892,7 +892,16 @@ static Result<void> 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<Service>& s) -> bool {
|
||||
if (s->is_from_apex()) {
|
||||
LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
|
||||
|
|
Loading…
Reference in a new issue