Android 12 init(2) rc脚本解析和事件执行流程
admin
2024-03-02 06:32:08
0

文章托管在gitee上 Android Notes , 同步csdn
本文基于Android12 分析

在init启动的SecondStage,通过调用LoadBootScripts来解析启动脚本,也就是rc文件。下面来研究这个解析过程,之后再看看init如何处理事件并执行相关action的。

SecondStageMain

int SecondStageMain(int argc, char** argv) {...const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();Action::set_function_map(&function_map); // 设置 Action 需要的函数映射表...InitializeSubcontext(); // 初始化SubcontextActionManager& am = ActionManager::GetInstance(); // 创建action管理器ServiceList& sm = ServiceList::GetInstance(); // 创建服务管理列表LoadBootScripts(am, sm); // 加载并解析启动脚本...
}

InitializeSubcontext

创建subcontext进程,用于执行init交给其的一些任务(比如后面执行的某些Command),通过socket与init进行通信

/// @system/core/init/subcontext.cpp
void InitializeSubcontext() {if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {subcontext.reset( // 创建Subcontext, path_prefixes为"/vendor", "/odm"new Subcontext(std::vector{"/vendor", "/odm"}, kVendorContext));}
}/// @system/core/init/subcontext.h
Subcontext(std::vector path_prefixes, std::string context, bool host = false): path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {if (!host) { /// host默认为falseFork(); // 创建subcontext进程}
}

LoadBootScripts

加载并解析 init rc 脚本

/// @system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {// 创建 Parser 对象Parser parser = CreateParser(action_manager, service_list);std::string bootscript = GetProperty("ro.boot.init_rc", "");//获取属性指定的rcif (bootscript.empty()) { // 没有指定rc,解析默认位置parser.ParseConfig("/system/etc/init/hw/init.rc"); // 首先解析 init.rcif (!parser.ParseConfig("/system/etc/init")) { // 解析 /system/etc/init 目录late_import_paths.emplace_back("/system/etc/init"); // 解析失败延时解析}// late_import is available only in Q and earlier release. As we don't// have system_ext in those versions, skip late_import for system_ext.parser.ParseConfig("/system_ext/etc/init"); // 解析 /system_ext/etc/init 目录if (!parser.ParseConfig("/vendor/etc/init")) { // 解析 /vendor/etc/init 目录late_import_paths.emplace_back("/vendor/etc/init");}if (!parser.ParseConfig("/odm/etc/init")) { // 解析 /odm/etc/init 目录late_import_paths.emplace_back("/odm/etc/init");}if (!parser.ParseConfig("/product/etc/init")) { // 解析 /product/etc/init 目录late_import_paths.emplace_back("/product/etc/init");}} else {parser.ParseConfig(bootscript);}
}

CreateParser

/// @system/core/init/init.cpp
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {Parser parser;// 添加各部分解析器// 创建ServiceParser 解析 service 服务,用于管理和启动native进程parser.AddSectionParser("service", std::make_unique(&service_list, GetSubcontext(), std::nullopt));// 创建ActionParser 解析 on action , 定义的一些动作                                           parser.AddSectionParser("on", std::make_unique(&action_manager, GetSubcontext()));// ImportParser解析import, 导入其他rcparser.AddSectionParser("import", std::make_unique(&parser));return parser;
}

AddSectionParser按名称添加parser到集合

/// @system/core/init/parser.cpp
void Parser::AddSectionParser(const std::string& name, std::unique_ptr parser) {section_parsers_[name] = std::move(parser); // 按名称存储对应的parser
}
// 集合按名字存储对应的 SectionParser
std::map> section_parsers_;

接下来看解析流程,通过调用Parser::ParseConfig函数来实现解析指定路径的rc文件或目录下的rc文件。

Parser::ParseConfig

/// @system/core/init/parser.cpp
bool Parser::ParseConfig(const std::string& path) {if (is_dir(path.c_str())) { // 如果提供的目录return ParseConfigDir(path); // 解析配置目录}return ParseConfigFile(path);  // 解析配置文件
}

Parser::ParseConfigDir

执行解析配置目录操作,遍历目录调用ParseConfigFile解析每一个文件。忽略目录下的子目录,也就是不支持多层级目录。

/// @system/core/init/parser.cpp
bool Parser::ParseConfigDir(const std::string& path) {LOG(INFO) << "Parsing directory " << path << "...";std::unique_ptr config_dir(opendir(path.c_str()), closedir);if (!config_dir) {PLOG(INFO) << "Could not import directory '" << path << "'";return false;}dirent* current_file;std::vector files;while ((current_file = readdir(config_dir.get()))) { // 读取目录下的内容// Ignore directories and only process regular files.if (current_file->d_type == DT_REG) { // 如果是文件则添加到待解析集合std::string current_path =android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);files.emplace_back(current_path);}}// Sort first so we load files in a consistent order (bug 31996208)std::sort(files.begin(), files.end());for (const auto& file : files) { // 遍历集合,解析文件if (!ParseConfigFile(file)) {LOG(ERROR) << "could not import file '" << file << "'";}}return true;
}

接下来看如何解析一个配置文件

Parser::ParseConfigFile

/// @system/core/init/parser.cpp
bool Parser::ParseConfigFile(const std::string& path) {LOG(INFO) << "Parsing file " << path << "...";android::base::Timer t;// 读取文件内容到string,实现在system/core/init/util.cpp,调用 android::base::ReadFdToStringauto config_contents = ReadFile(path);if (!config_contents.ok()) {LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();return false;}ParseData(path, &config_contents.value()); // 解析脚本内容LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";return true;
}

Parser::ParseData

解析流程循环:

  • 获取的下一个token来决定接下来的动作,比如EOF结尾或者某个关键字
  • 遇到T_EOF则调用EndSection结束当前解析的ParseSection,退出循环
  • 遇到T_NEWLINE换新行,则进行解析一行
  • 如果获取的token是parser对应的关键字,比如on,则获取对应的parser进行解析,调用ParseSection
  • 如果token不是关键字,且上一步有获取parser,则进行解析一行,调用ParseLineSection
  • 遇到下一个关键字则调用EndSection结束当前解析的ParseSection,开启新的解析
  • 如果解析到文本,则添加到args集合
void Parser::ParseData(const std::string& filename, std::string* data) {data->push_back('\n'); // 添加一个换行data->push_back('\0'); // 添加一个结束符parse_state state;state.line = 0;state.ptr = data->data();state.nexttoken = 0;SectionParser* section_parser = nullptr;int section_start_line = -1;std::vector args;// If we encounter a bad section start, there is no valid parser object to parse the subsequent// sections, so we must suppress errors until the next valid section is found.bool bad_section_found = false;auto end_section = [&] { // 结束section的回调bad_section_found = false;if (section_parser == nullptr) return;if (auto result = section_parser->EndSection(); !result.ok()) {parse_error_count_++;LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();}section_parser = nullptr;section_start_line = -1;};for (;;) {switch (next_token(&state)) { // 根据获取的下一个token来决定接下来的动作case T_EOF: // 文件结束。end_section();for (const auto& [section_name, section_parser] : section_parsers_) {section_parser->EndFile();}return;case T_NEWLINE: { // 检测到换行,解析当前行state.line++;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.auto line_callback = std::find_if(line_callbacks_.begin(), line_callbacks_.end(),[&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });if (line_callback != line_callbacks_.end()) { // 针对指定prefix的情况,比如 /dev/end_section();if (auto result = line_callback->second(std::move(args)); !result.ok()) {parse_error_count_++;LOG(ERROR) << filename << ": " << state.line << ": " << result.error();}} else if (section_parsers_.count(args[0])) { // 如果第一个值能够匹配到parser, 比如是 serviceend_section(); // 结束之前。section_parser = section_parsers_[args[0]].get();section_start_line = state.line;if (auto result = // 解析 sectionsection_parser->ParseSection(std::move(args), filename, state.line);!result.ok()) {parse_error_count_++;LOG(ERROR) << filename << ": " << state.line << ": " << result.error();section_parser = nullptr;bad_section_found = true;}} else if (section_parser) { // 使用当前parser解析一行if (auto result = section_parser->ParseLineSection(std::move(args), state.line);!result.ok()) {parse_error_count_++;LOG(ERROR) << filename << ": " << state.line << ": " << result.error();}} else if (!bad_section_found) { // 没有任何匹配,则表示关键字错误parse_error_count_++;LOG(ERROR) << filename << ": " << state.line<< ": Invalid section keyword found";}args.clear();break;}case T_TEXT: // 是文本则添加到vectorargs.emplace_back(state.text);break;}}
}

next_token

该函数是用来解析token的,并获取有效的文本信息

/// @system/core/init/tokenizer.cpp
int next_token(struct parse_state *state)
{char *x = state->ptr;char *s;if (state->nexttoken) {int t = state->nexttoken;state->nexttoken = 0;return t;}for (;;) {switch (*x) {case 0: // 结尾state->ptr = x;return T_EOF;case '\n': // 换行符x++;state->ptr = x;return T_NEWLINE;case ' ':case '\t':case '\r': // 跳过空白字符x++;continue;case '#':while (*x && (*x != '\n')) x++; // 跳过 # 注释行if (*x == '\n') { // 换行state->ptr = x+1;return T_NEWLINE;} else { // 结尾state->ptr = x;return T_EOF;}default: // 默认跳转到文本处理goto text;}}textdone:state->ptr = x;*s = 0;return T_TEXT;
text:state->text = s = x;
textresume:for (;;) {switch (*x) {case 0: // 字符结束符goto textdone;case ' ':case '\t':case '\r': // 检测到空白符则返回x++;goto textdone;case '\n': // 换行state->nexttoken = T_NEWLINE;x++;goto textdone;case '"': // 引号包裹内存x++;for (;;) {switch (*x) {case 0:/* unterminated quoted thing */state->ptr = x;return T_EOF;case '"':x++;goto textresume;default:*s++ = *x++; // 复制引号内容}}break;case '\\':  // 下面处理其他转义字符x++;switch (*x) {case 0:goto textdone;case 'n':*s++ = '\n';x++;break;case 'r':*s++ = '\r';x++;break;case 't':*s++ = '\t';x++;break;case '\\':*s++ = '\\';x++;break;case '\r':/* \   -> line continuation */if (x[1] != '\n') {x++;continue;}x++;FALLTHROUGH_INTENDED;case '\n':/* \  -> line continuation */state->line++;x++;/* eat any extra whitespace */while((*x == ' ') || (*x == '\t')) x++;continue;default:/* unknown escape -- just copy */*s++ = *x++;}continue;default:*s++ = *x++;}}return T_EOF;
}

接下来以解析on为例来分析,看关键字对应的是ActionParser

ActionParser::ParseSection

/// @system/core/init/action_parser.cpp
Result ActionParser::ParseSection(std::vector&& args,const std::string& filename, int line) {std::vector triggers(args.begin() + 1, args.end()); // 除了on的其他参数if (triggers.size() < 1) { // on 后面需要指定triggerreturn Error() << "Actions must have a trigger";}Subcontext* action_subcontext = nullptr;// 检查路径是否匹配path_prefixes_中的路径前缀 /vendor, /odmif (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {action_subcontext = subcontext_; // 前缀匹配会获取到 subcontext_}std::string event_trigger;std::map property_triggers;if (auto result = // 解析 triggerParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);!result.ok()) {return Error() << "ParseTriggers() failed: " << result.error();}// 创建该action对象,第一个参数false表示非oneshot,auto action = std::make_unique(false, action_subcontext, filename, line, event_trigger,property_triggers);action_ = std::move(action); // 缓存此解析的action,后续解析line的时候会用到return {};
}

ParseTriggers

以解析 on post-fs-data && property:ro.debuggable=1 为例分析,args 就是on 之后的内容。

Result ParseTriggers(const std::vector& args, Subcontext* subcontext,std::string* event_trigger,std::map* property_triggers) {const static std::string prop_str("property:");//属性触发器前缀for (std::size_t i = 0; i < args.size(); ++i) {if (args[i].empty()) {return Error() << "empty trigger is not valid";}if (i % 2) { // 按 && 将各个触发器连接起来,奇数位的是 &&if (args[i] != "&&") {return Error() << "&& is the only symbol allowed to concatenate actions";} else {continue;}}// property: 开头的是属性触发器if (!args[i].compare(0, prop_str.length(), prop_str)) {if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers); // 解析属性触发器!result.ok()) {return result;}} else { // 否则是事件触发器,比如 post-fs-dataif (!event_trigger->empty()) { // 只能指定一个事件触发器return Error() << "multiple event triggers are not allowed";}// 检查组成字符合法性,通常只能由 _ 、- 、数字或字母 组成if (auto result = ValidateEventTrigger(args[i]); !result.ok()) {return result;}*event_trigger = args[i]; // 保存事件触发器}}return {};
}

ParsePropertyTrigger

Result ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,std::map* property_triggers) {const static std::string prop_str("property:"); // property:ro.debuggable=1std::string prop_name(trigger.substr(prop_str.length()));// 去除前缀 ro.debuggable=1size_t equal_pos = prop_name.find('=');// 找到 =if (equal_pos == std::string::npos) {return Error() << "property trigger found without matching '='";}std::string prop_value(prop_name.substr(equal_pos + 1));// = 后面的是值prop_name.erase(equal_pos); // 等号前面的是属性名if (!IsActionableProperty(subcontext, prop_name)) {// 检查属性是否支持触发器return Error() << "unexported property trigger found: " << prop_name;}// 将属性名和值组成的触发器添加到 property_triggersif (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {return Error() << "multiple property triggers found for same property";}return {};
}

到此,已经创建了Action,并获取了事件触发器和属性触发器。接下来解析它的Command命令列表

ParseLineSection

/// @system/core/init/action_parser.cpp
Result ActionParser::ParseLineSection(std::vector&& args, int line) {return action_ ? action_->AddCommand(std::move(args), line) : Result{};// 通过Action解析Command
}

Action::AddCommand

/// @system/core/init/action.cpp
Result Action::AddCommand(std::vector&& args, int line) {if (!function_map_) {return Error() << "no function map available";}auto map_result = function_map_->Find(args); // 匹配函数表if (!map_result.ok()) {return Error() << map_result.error();}// 找到匹配的函数,将新创建的 Command 添加到commands_列表commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),line);return {};
}

创建 Command

Command::Command(BuiltinFunction f, bool execute_in_subcontext, std::vector&& args,int line): func_(std::move(f)), // 函数execute_in_subcontext_(execute_in_subcontext), // 是否在subcontext中执行args_(std::move(args)), // 参数line_(line) /* 命令在文件行号*/ {}

function_map_ 实际上是KeywordMap的实例,看看它的实现。

KeywordMap::Find

Result Find(const std::vector& args) const {if (args.empty()) return Error() << "Keyword needed, but not provided";// 以 copy /proc/cmdline /dev/urandom 为例auto& keyword = args[0]; // 取出关键字 copyauto num_args = args.size() - 1; // 关键字copy 后的其他参数auto result_it = map_.find(keyword); // 找到copy对应的 MapValueif (result_it == map_.end()) {return Errorf("Invalid keyword '{}'", keyword);}auto result = result_it->second; // MapValueauto min_args = result.min_args;auto max_args = result.max_args;if (min_args == max_args && num_args != min_args) {// 检查参数的数量return Errorf("{} requires {} argument{}", keyword, min_args,(min_args > 1 || min_args == 0) ? "s" : "");}if (num_args < min_args || num_args > max_args) { // 检查是否缺参数,多参数if (max_args == std::numeric_limits::max()) {return Errorf("{} requires at least {} argument{}", keyword, min_args,min_args > 1 ? "s" : "");} else {return Errorf("{} requires between {} and {} arguments", keyword, min_args,max_args);}}return result.value;
}

看一下KeywordMap的实现

// Value is the return value of Find(), which is typically either a single function or a struct with
// additional information.
template 
class KeywordMap {public:struct MapValue {size_t min_args;size_t max_args;Value value;};KeywordMap() {}KeywordMap(std::initializer_list> init) : map_(init) {}Result Find(const std::vector& args) const { ... }private:std::map map_; // 函数名+MapValue组合
};

BuiltinFunctionMap

function_map_ 是一些内置函数映射表,通过GetBuiltinFunctionMap(),然后通过Action::set_function_map设置进来的。BuiltinFunctionMap 是KeywordMap的别名。

/// @system/core/init/builtins.h
using BuiltinFunction = std::function(const BuiltinArguments&)>;struct BuiltinFunctionMapValue {bool run_in_subcontext;BuiltinFunction function;
};using BuiltinFunctionMap = KeywordMap;/// 内置函数映射表,当Command执行 copy 时, 会调用 do_copy 函数
/// @system/core/init/builtins.cpp
// Builtin-function-map start
const BuiltinFunctionMap& GetBuiltinFunctionMap() {constexpr std::size_t kMax = std::numeric_limits::max();// clang-format offstatic const BuiltinFunctionMap builtin_functions =// 函数名, MapValue{ min_args,max_args,BuiltinFunctionMapValue{ run_in_subcontext, BuiltinFunction } }{"bootchart",               {1,     1,    {false,  do_bootchart}}},{"chmod",                   {2,     2,    {true,   do_chmod}}},{"chown",                   {2,     3,    {true,   do_chown}}},{"class_reset",             {1,     1,    {false,  do_class_reset}}},{"class_reset_post_data",   {1,     1,    {false,  do_class_reset_post_data}}},{"class_restart",           {1,     1,    {false,  do_class_restart}}},{"class_start",             {1,     1,    {false,  do_class_start}}},{"class_start_post_data",   {1,     1,    {false,  do_class_start_post_data}}},{"class_stop",              {1,     1,    {false,  do_class_stop}}},{"copy",                    {2,     2,    {true,   do_copy}}},{"copy_per_line",           {2,     2,    {true,   do_copy_per_line}}},{"domainname",              {1,     1,    {true,   do_domainname}}},{"enable",                  {1,     1,    {false,  do_enable}}},{"exec",                    {1,     kMax, {false,  do_exec}}},{"exec_background",         {1,     kMax, {false,  do_exec_background}}},{"exec_start",              {1,     1,    {false,  do_exec_start}}},{"export",                  {2,     2,    {false,  do_export}}},{"hostname",                {1,     1,    {true,   do_hostname}}},{"ifup",                    {1,     1,    {true,   do_ifup}}},{"init_user0",              {0,     0,    {false,  do_init_user0}}},{"insmod",                  {1,     kMax, {true,   do_insmod}}},{"installkey",              {1,     1,    {false,  do_installkey}}},{"interface_restart",       {1,     1,    {false,  do_interface_restart}}},{"interface_start",         {1,     1,    {false,  do_interface_start}}},{"interface_stop",          {1,     1,    {false,  do_interface_stop}}},{"load_exports",            {1,     1,    {false,  do_load_exports}}},{"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},{"load_system_props",       {0,     0,    {false,  do_load_system_props}}},{"loglevel",                {1,     1,    {false,  do_loglevel}}},{"mark_post_data",          {0,     0,    {false,  do_mark_post_data}}},{"mkdir",                   {1,     6,    {true,   do_mkdir}}},// TODO: Do mount operations in vendor_init.// mount_all is currently too complex to run in vendor_init as it queues action triggers,// imports rc scripts, etc.  It should be simplified and run in vendor_init context.// mount and umount are run in the same context as mount_all for symmetry.{"mount_all",               {0,     kMax, {false,  do_mount_all}}},{"mount",                   {3,     kMax, {false,  do_mount}}},{"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},{"umount",                  {1,     1,    {false,  do_umount}}},{"umount_all",              {0,     1,    {false,  do_umount_all}}},{"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},{"readahead",               {1,     2,    {true,   do_readahead}}},{"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},{"restart",                 {1,     1,    {false,  do_restart}}},{"restorecon",              {1,     kMax, {true,   do_restorecon}}},{"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},{"rm",                      {1,     1,    {true,   do_rm}}},{"rmdir",                   {1,     1,    {true,   do_rmdir}}},{"setprop",                 {2,     2,    {true,   do_setprop}}},{"setrlimit",               {3,     3,    {false,  do_setrlimit}}},{"start",                   {1,     1,    {false,  do_start}}},{"stop",                    {1,     1,    {false,  do_stop}}},{"swapon_all",              {0,     1,    {false,  do_swapon_all}}},{"enter_default_mount_ns",  {0,     0,    {false,  do_enter_default_mount_ns}}},{"symlink",                 {2,     2,    {true,   do_symlink}}},{"sysclktz",                {1,     1,    {false,  do_sysclktz}}},{"trigger",                 {1,     1,    {false,  do_trigger}}},{"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},{"wait",                    {1,     2,    {true,   do_wait}}},{"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},{"write",                   {2,     2,    {true,   do_write}}},};// clang-format onreturn builtin_functions;
}
// Builtin-function-map end

当解析完一个action,会执行EndSection函数

ActionParser::EndSection

Result ActionParser::EndSection() {if (action_ && action_->NumCommands() > 0) {action_manager_->AddAction(std::move(action_)); // 将当前解析的action添加到集合}return {};
}

动作、内置动作和事件触发器

  • 动作(Action)
    通常是在rc中使用 on 声明的动作,action通常需要一些事件来触发
  • 内置动作(Builtin Action)
    只在代码里面调用QueueBuiltinAction的action,其他action在rc里使用 on 声明。
  • 事件触发器(Trigger)
    调用QueueEventTrigger插入事件触发器。这种触发器通常用一个字符串表示,会有一些action与之对应。

看下 Action 构造函数

Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,const std::string& event_trigger,const std::map& property_triggers): property_triggers_(property_triggers),//属性触发器集合event_trigger_(event_trigger), // 事件触发器名字oneshot_(oneshot), // 一次性标记subcontext_(subcontext), // 执行上下文filename_(filename), // action所在文件和行号line_(line) {}

action通常在事件发生时触发,事件都存放在event_queue_集合里

std::vector> actions_; // action集合
std::queue> event_queue_GUARDED_BY(event_queue_lock_); // 事件队列
mutable std::mutex event_queue_lock_;
std::queue current_executing_actions_; // 正在出现的action队列
std::size_t current_command_; // 已执行的命令数量

ActionManager::QueueBuiltinAction

void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {auto lock = std::lock_guard{event_queue_lock_};// 创建action,注意第一个参数为true 表示一次性的 oneshotauto action = std::make_unique(true, nullptr, "", 0, name,std::map{});action->AddCommand(std::move(func), {name}, 0);// fun 添加到命令列表,执行action的命令时会被调用event_queue_.emplace(action.get());  // 添加到事件队列actions_.emplace_back(std::move(action)); // 添加到action队列
}

ActionManager::QueueEventTrigger

void ActionManager::QueueEventTrigger(const std::string& trigger) {auto lock = std::lock_guard{event_queue_lock_};event_queue_.emplace(trigger); // 添加到事件队列
}

QueueBuiltinAction和QueueEventTrigger只是将相关action和事件添加进队列,实际上没有真正去执行,而实际去执行是在其主循环里面触发的,通过 ActionManager#ExecuteOneCommand

ActionManager::ExecuteOneCommand

/// @system/core/init/action_manager.cpp
void ActionManager::ExecuteOneCommand() {{auto lock = std::lock_guard{event_queue_lock_};// Loop through the event queue until we have an action to executewhile (current_executing_actions_.empty() && !event_queue_.empty()) { // 当前没有执行的action,但是有事件for (const auto& action : actions_) { // 找到此事件对应要处理的action,可能对应多个if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },event_queue_.front())) {current_executing_actions_.emplace(action.get()); // 一个事件可以对应多个 action}}event_queue_.pop();}}if (current_executing_actions_.empty()) {return;}// 返回 queue 中第一个元素的引用,而不是删除auto action = current_executing_actions_.front();if (current_command_ == 0) { // 处理此action的第一条命令时打印std::string trigger_name = action->BuildTriggersString();LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()<< ":" << action->line() << ")";}action->ExecuteOneCommand(current_command_); // 回调每个action匹配的命令// If this was the last command in the current action, then remove// the action from the executing list.// If this action was oneshot, then also remove it from actions_.++current_command_;  // 每执行一次数量加1if (current_command_ == action->NumCommands()) { // 如果此 action的命令全部执行完毕current_executing_actions_.pop(); // 从正执行列表删除current_command_ = 0;if (action->oneshot()) { // 如果是一次性的 还有从action列表移除。 通常Builtin Action是一次性的auto eraser = [&action](std::unique_ptr& a) { return a.get() == action; };actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),actions_.end());}}
}

Action::CheckEvent

检查event 与 action 是否匹配

bool Action::CheckEvent(const EventTrigger& event_trigger) const { // 检查事件触发器return event_trigger == event_trigger_ && CheckPropertyTriggers();//触发器满足并且所有实现也匹配
}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 { // 检查内置action,其event和action是同一个对象return this == builtin_action;
}// This function checks that all property triggers are satisfied, that is
// for each (name, value) in property_triggers_, check that the current
// value of the property 'name' == value.
//
// It takes an optional (name, value) pair, which if provided must
// be present in property_triggers_; it skips the check of the current
// property value for this pair.
bool Action::CheckPropertyTriggers(const std::string& name, const std::string& value) const {// 检查所有属性是否匹配if (property_triggers_.empty()) { // 需要包含属性trigger -> property:aa.bb.cc=xxxreturn true;}if (!name.empty()) { // 如果指定key,需要检查value是否满足指定触发值auto it = property_triggers_.find(name);if (it == property_triggers_.end()) {return false;}const auto& trigger_value = it->second;if (trigger_value != "*" && trigger_value != value) {return false;}}// 指定值满足 或 指定值为空。 判断所有非指定的属性键值要满足触发值for (const auto& [trigger_name, trigger_value] : property_triggers_) {if (trigger_name != name) { // 判断除了指定触发键之外的std::string prop_value = android::base::GetProperty(trigger_name, "");if (trigger_value == "*" && !prop_value.empty()) {continue;}if (trigger_value != prop_value) return false;}}return true;
}

Action::ExecuteOneCommand

执行一条Command

/// @system/core/init/action.cpp
void Action::ExecuteOneCommand(std::size_t command) const {// We need a copy here since some Command execution may result in// changing commands_ vector by importing .rc files through parserCommand cmd = commands_[command];ExecuteCommand(cmd);
}// 执行某一条命令
void Action::ExecuteCommand(const Command& command) const {android::base::Timer t;auto result = command.InvokeFunc(subcontext_); // 调用执行对应的命令auto duration = t.duration();// Any action longer than 50ms will be warned to user as slow operationif (!result.has_value() || duration > 50ms ||android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {std::string trigger_name = BuildTriggersString();std::string cmd_str = command.BuildCommandString();LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_<< ":" << command.line() << ") took " << duration.count() << "ms and "<< (result.ok() ? "succeeded" : "failed: " + result.error().message());}
}

Command::InvokeFunc

/// @system/core/init/action.cpp
Result Command::InvokeFunc(Subcontext* subcontext) const {if (subcontext) { // 指定了subcontextif (execute_in_subcontext_) { // 如果指定在 subcontext 下执行return subcontext->Execute(args_);}auto expanded_args = subcontext->ExpandArgs(args_);if (!expanded_args.ok()) { // 膨胀处理失败,则结束处理return expanded_args.error();}return RunBuiltinFunction(func_, *expanded_args, subcontext->context());}return RunBuiltinFunction(func_, args_, kInitContext);
}
  • 当指定了不为空的subcontext,
  • 若execute_in_subcontext_为true,则会直接调用subcontext->Execute
  • 否则会先subcontext->ExpandArgs通过subcontext进程对args进行膨胀处理(主要是将 ${prop_name} 解析成对应的属性值),然后在调用RunBuiltinFunction
  • 未指定subcontext则直接调用RunBuiltinFunction

RunBuiltinFunction

执行内置的函数

Result RunBuiltinFunction(const BuiltinFunction& function,const std::vector& args, const std::string& context) {auto builtin_arguments = BuiltinArguments(context);builtin_arguments.args.resize(args.size());builtin_arguments.args[0] = args[0];for (std::size_t i = 1; i < args.size(); ++i) {// ExpandProps函数将属性 ${prop_name} 解析成对应的属性值,在system/core/init/util.cpp 实现auto expanded_arg = ExpandProps(args[i]);if (!expanded_arg.ok()) {return expanded_arg.error();}builtin_arguments.args[i] = std::move(*expanded_arg);}return function(builtin_arguments); // 调用对应的函数
}

比如对于如下Command,则会调用do_setprop去将vendor.qemu.timezone的属性值设置给persist.sys.timezone。

setprop persist.sys.timezone ${vendor.qemu.timezone}

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...