将kernel cmdline的属性,设置为"ro.boot."/"ro.kernel."
我们在cmdline自定义的属性也可以通过这种方式传递给上层,
我们看一下源码是如何实现的:
Android8.1/9.0:
static char qemu[32];
int main(int argc, char** argv)
->process_kernel_cmdline();
->import_kernel_cmdline(false, import_kernel_nv);
->android::base::ReadFileToString("/proc/cmdline", &cmdline);
->for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) { //遍历cmdline
->std::vector<std::string> pieces = android::base::Split(entry, "="); //字符串分隔成 “名字” = “值”
->fn(pieces[0], pieces[1], in_qemu);
->import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
->if (key == "qemu") { //模拟器的属性
->strlcpy(qemu, value.c_str(), sizeof(qemu)); //设置模拟器标志位
->else if (android::base::StartsWith(key, "androidboot.")) { //如果是一般的"androidboot."属性,全部设置为"ro.boot."
->property_set("ro.boot." + key.substr(12), value);
->if (qemu[0]) //如果模拟器存在
->import_kernel_cmdline(true, import_kernel_nv);
->for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) { //再次遍历cmdline
->std::vector<std::string> pieces = android::base::Split(entry, "="); //字符串分隔成 “名字” = “值”
->fn(pieces[0], pieces[1], in_qemu);
->import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
->if (for_emulator) {
->property_set("ro.kernel." + key, value); //将cmdline的所有属性都设置为"ro.kernel."
->return;
/system/core/init/init.cpp
1 2 3 4 5 6 7 8 9 | static char qemu[32]; int main(int argc, char** argv) { ... ... property_init(); ... ... process_kernel_dt(); process_kernel_cmdline(); ... ... } |
/system/core/init/init.cpp
1 2 3 4 5 6 7 | static void process_kernel_cmdline() { // The first pass does the common stuff, and finds if we are in qemu. // The second pass is only necessary for qemu to export all kernel params // as properties. import_kernel_cmdline(false, import_kernel_nv); if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv); } |
/system/core/init/util.cpp
1 2 3 4 5 6 7 8 9 10 11 12 | void import_kernel_cmdline(bool in_qemu, const std::function<void(const std::string&, const std::string&, bool)>& fn) { std::string cmdline; android::base::ReadFileToString("/proc/cmdline", &cmdline);
for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) { std::vector<std::string> pieces = android::base::Split(entry, "="); if (pieces.size() == 2) { fn(pieces[0], pieces[1], in_qemu); } } } |
/system/core/init/init.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) { if (key.empty()) return;
if (for_emulator) { // In the emulator, export any kernel option with the "ro.kernel." prefix. property_set("ro.kernel." + key, value); return; }
if (key == "qemu") { strlcpy(qemu, value.c_str(), sizeof(qemu)); } else if (android::base::StartsWith(key, "androidboot.")) { property_set("ro.boot." + key.substr(12), value); } } |
Android 10.0之后:
将之前的main函数分成了几部分,读取cmdline并设置ro.boot属性的功能放到了SecondStageMain()函数
int main(int argc, char** argv)
->SecondStageMain()
->process_kernel_cmdline(); //以下流程和8.1/9.0差不多
->import_kernel_cmdline
->... ...
/system/core/init/main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | int main(int argc, char** argv) { #if __has_feature(address_sanitizer) __asan_set_error_report_callback(AsanReportCallback); #endif // Boost prio which will be restored later setpriority(PRIO_PROCESS, 0, -20); if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); }
if (argc > 1) { if (!strcmp(argv[1], "subcontext")) { android::base::InitLogging(argv, &android::base::KernelLogger); const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map); }
if (!strcmp(argv[1], "selinux_setup")) { return SetupSelinux(argv); }
if (!strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv); } }
return FirstStageMain(argc, argv); } |
Android11.0之后,调用流程再次做了一点改动:
int main(int argc, char** argv)
->SecondStageMain() /system/core/init/main.cpp
->PropertyInit(); /system/core/init/property_service.cpp
->process_kernel_cmdline(); /system/core/init/property_service.cpp //以下流程没有变化
->ImportKernelCmdline /system/core/init/util.cpp
->... ...
/system/core/init/property_service.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | void PropertyInit() { selinux_callback cb; cb.func_audit = PropertyAuditCallback; selinux_set_callback(SELINUX_CB_AUDIT, cb);
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); CreateSerializedPropertyInfo(); if (__system_property_area_init()) { LOG(FATAL) << "Failed to initialize property area"; } if (!property_info_area.LoadDefaultPath()) { LOG(FATAL) << "Failed to load serialized property info file"; }
// If arguments are passed both on the command line and in DT, // properties set in DT always have priority over the command-line ones. ProcessKernelDt(); ProcessKernelCmdline();
// Propagate the kernel variables to internal variables // used by init as well as the current required properties. ExportKernelBootProps();
PropertyLoadBootDefaults(); } |
/system/core/init/property_service.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static void ProcessKernelCmdline() { bool for_emulator = false; ImportKernelCmdline([&](const std::string& key, const std::string& value) { //以前的import_kernel_nv()现在变成了匿名函数 if (key == "qemu") { for_emulator = true; } else if (StartsWith(key, "androidboot.")) { InitPropertySet("ro.boot." + key.substr(12), value); } });
if (for_emulator) { ImportKernelCmdline([&](const std::string& key, const std::string& value) { // In the emulator, export any kernel option with the "ro.kernel." prefix. InitPropertySet("ro.kernel." + key, value); }); } } |
/system/core/init/util.cpp
1 2 3 4 5 6 7 8 9 10 11 | void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>& fn) { std::string cmdline; android::base::ReadFileToString("/proc/cmdline", &cmdline);
for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) { std::vector<std::string> pieces = android::base::Split(entry, "="); if (pieces.size() == 2) { fn(pieces[0], pieces[1]); } } } |