APEX configs support 'on' as well am: badb7de1a2
Original change: https://android-review.googlesource.com/c/platform/system/core/+/2093004 Change-Id: Iab5bc7325e65c464d42e775d724e7e834671081a Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
bc3ec37dad
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