安卓系统属性之androidboot.xxx转换成ro.boot.xxx

目录

  • 前言
  • 一、init进程-->SecondStageMain
  • 二、process_kernel_dt
    • [2.1 is_android_dt_value_expected](#2.1 is_android_dt_value_expected)
      • [2.1.1 read_android_dt_file](#2.1.1 read_android_dt_file)
      • [2.1.2 get_android_dt_dir](#2.1.2 get_android_dt_dir)
      • [2.1.3 init_android_dt_dir](#2.1.3 init_android_dt_dir)
      • [2.1.4 import_kernel_cmdline](#2.1.4 import_kernel_cmdline)
    • [2.2 property_set()设置属性](#2.2 property_set()设置属性)
  • 三、process_kernel_cmdline
    • [3.1 import_kernel_cmdline(false, import_kernel_nv);](#3.1 import_kernel_cmdline(false, import_kernel_nv);)
    • [3.2 import_kernel_cmdline(true, import_kernel_nv);](#3.2 import_kernel_cmdline(true, import_kernel_nv);)
  • 四、export_kernel_boot_props
  • 五、总结

前言

kermel cmdline中我们经常会设置androidboot.xxx比如androidboot.selinux=disabled

但是代码中并不能搜到对应的内容,其实是init进程做了统一转换。

一、init进程-->SecondStageMain

init进程的SecondStageMain阶段,执行了process_kernel_dtprocess_kernel_cmdline

c 复制代码
system\core\init\main.cpp
c 复制代码
int main(int argc, char** argv) {
......
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }
......
    return FirstStageMain(argc, argv);
}
c 复制代码
int SecondStageMain(int argc, char** argv) {
......
    property_init();

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();
    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();

......

翻译一下:

如果参数既在命令行中传递,又在DT中传递,则DT中设置的属性始终优先于命令行中的属性。

将内核命令行传播到由init进程使用的内部变量以及当前所需的属性中。


二、process_kernel_dt

c 复制代码
static void process_kernel_dt() {
    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
        return;
    }

    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
    if (!dir) return;

    std::string dt_file;
    struct dirent *dp;
    while ((dp = readdir(dir.get())) != NULL) {
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
            continue;
        }

        std::string file_name = get_android_dt_dir() + dp->d_name;

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        property_set("ro.boot."s + dp->d_name, dt_file);
    }
}

2.1 is_android_dt_value_expected

c 复制代码
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
    return;
}

从方法的名字上也可以推测出来,检查设备树(Device Tree)中的某个属性(这里是 "compatible")的值是否符合预期(这里是 "android,firmware")。如果不是,直接return。

c 复制代码
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
    std::string dt_content;
    if (read_android_dt_file(sub_path, &dt_content)) {
        if (dt_content == expected_content) {
            return true;
        }
    }
    return false;
}

"从设备树的compatible路径读取内容,并判断内容是否与android,firmware完全相等。"

如果相等,返回true;否则返回false

2.1.1 read_android_dt_file

c 复制代码
// Reads the content of device tree file under the platform's Android DT directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
    const std::string file_name = get_android_dt_dir() + sub_path;
    if (android::base::ReadFileToString(file_name, dt_content)) {
        if (!dt_content->empty()) {
            dt_content->pop_back();  // Trims the trailing '\0' out.
            return true;
        }
    }
    return false;
}

先来翻译一下:

读取平台Android DT目录下的设备树文件内容。如果读取成功,则返回true,否则返回false

  • 1.构建完整文件路径file_name;
  • 2.调用ReadFileToString()读取文件;
  • 3.如果读取成功,且内容不为空:
    删除内容末尾的\0字符;
    返回true;
    否则,返回false。

2.1.2 get_android_dt_dir

c 复制代码
// FIXME: The same logic is duplicated in system/core/fs_mgr/
const std::string& get_android_dt_dir() {
    // Set once and saves time for subsequent calls to this function
    static const std::string kAndroidDtDir = init_android_dt_dir();
    return kAndroidDtDir;
}

直接调用init_android_dt_dir初始化设备树目录,然后返回这个目录

2.1.3 init_android_dt_dir

c 复制代码
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");

static std::string init_android_dt_dir() {
    // Use the standard procfs-based path by default
    std::string android_dt_dir = kDefaultAndroidDtDir;
    // The platform may specify a custom Android DT path in kernel cmdline
    import_kernel_cmdline(false,
                          [&](const std::string& key, const std::string& value, bool in_qemu) {
                              if (key == "androidboot.android_dt_dir") {
                                  android_dt_dir = value;
                              }
                          });
    LOG(INFO) << "Using Android DT directory " << android_dt_dir;
    return android_dt_dir;
}
  • 初始化Android设备树(Device Tree)目录路径
  • 默认路径为:/proc/device-tree/firmware/android/
  • 如果内核启动参数(cmdline)中指定了androidboot.android_dt_dir,则会覆盖默认路径,
    这里使用了lambda表达式

2.1.4 import_kernel_cmdline

c 复制代码
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);
        }
    }
}
  • /proc/cmdline读取内核启动参数字符串。
  • 解析出所有参数(空格分隔的字符串,例如:param1=val1 param2=val2 ...)。
  • 对每个参数,调用提供的回调函数fn,传入参数名、值和标志位in_qemu

看一下我本地的路径以及其对应的值

c 复制代码
Android:/proc/device-tree/firmware/android # ls
compatible hardware mode name serialno

Android:/proc/device-tree/firmware/android # cat compatible
android,firmware

2.2 property_set()设置属性

c 复制代码
static void process_kernel_dt() {
    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
        return;
    }

    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
    if (!dir) return;

    std::string dt_file;
    struct dirent *dp;
    while ((dp = readdir(dir.get())) != NULL) {
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
            continue;
        }

        std::string file_name = get_android_dt_dir() + dp->d_name;

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        property_set("ro.boot."s + dp->d_name, dt_file);
    }
}

然后这个目录下以每个文件名作为属性(除了compatible 和name),文件里面的内容作为属性值。这里的话就是ro.boot.harewarero.boot.modero.boot.serialno这三个属性值

c 复制代码
Android:/proc/device-tree/firmware/android # ls
compatible hardware mode name serialno
Android:/proc/device-tree/firmware/android #

三、process_kernel_cmdline

c 复制代码
static char qemu[32];

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);
}

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);
        }
    }
}


static void import_kernel_nv(char *name, bool for_emulator)
{
    char *value = strchr(name, '=');
    int name_len = strlen(name);

    if (value == 0) return;
    *value++ = 0;
    if (name_len == 0) return;

    if (for_emulator) {
        /* in the emulator, export any kernel option with the
         * ro.kernel. prefix */
        char buff[PROP_NAME_MAX];
        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );

        if (len < (int)sizeof(buff))
            property_set( buff, value );
        return;
    }

    if (!strcmp(name,"qemu")) {
        strlcpy(qemu, value, sizeof(qemu));
    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
        const char *boot_prop_name = name + 12;
        char prop[PROP_NAME_MAX];
        int cnt;

        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
        if (cnt < PROP_NAME_MAX)
            property_set(prop, value);
    }
}
  • 调用import_kernel_cmdline函数,上面已经讲过,就是读取proc/cmdline中的内容。这个函数有两个参数,第一个采纳数标识当前Android是否是模拟器。第二个参数是一个函数指针;主要指的是通过调用import_kernel_nv函数来设置系统属性。

3.1 import_kernel_cmdline(false, import_kernel_nv);

import_kernel_cmdline 第一次执行时,传入import_kernel_nv的形参为for_emulator为0,

因此将匹配name是否为qemu。

  • 如果是,将其值保存在qemu全局静态缓冲区中。
    对于android模拟器,存在/proc/cmdline中存在"qemu=1"字段
    如果for_emulator为1,则将生成 ro.kernel.{name}={value} 属性写入Android属性系统中。
  • 如果不是,则将/proc/cmdline中以androidboot.为开头的键值对,以ro.boot替换,添加到系统属性中。

此时回到process_kernel_cmdline函数,继续执行

3.2 import_kernel_cmdline(true, import_kernel_nv);

当系统为模拟器时,qemu[0]其值为"1",第二次执行import_kernel_cmdline函数,将再次调用import_kernel_nv函数,生成ro.kernel.xxx属性。


四、export_kernel_boot_props

c 复制代码
static void export_kernel_boot_props() {
    constexpr const char* UNSET = "";
    struct {
        const char *src_prop;
        const char *dst_prop;
        const char *default_value;
    } prop_map[] = {
        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
    };
    for (const auto& prop : prop_map) {
        std::string value = GetProperty(prop.src_prop, prop.default_value);
        if (value != UNSET)
            property_set(prop.dst_prop, value);
    }
}
  • 定义一份"映射表"prop_map,列出需要读取和设置的属性
  • 循环处理每个映射:
    从源属性获取值(如果未定义则使用默认值)
    如果这个值不是空(UNSET空定义),就设置到目标属性。

其实就是将已有的ro.boot.xxx属性键值对设置到了ro.xxx属性键值对中。


五、总结

  1. process_kernel_dt()
    作用

    判断设备树中的compatible属性是否为"android,firmware",确保硬件符合预期。

    遍历设备树目录中的文件(排除特殊文件如compatiblename)。

    读取每个文件的内容,将其中的逗号,替换为点.,并设置为系统属性(ro.boot.*)。
    主要目的

    从设备树采集硬件描述信息(比如硬件版本、型号等),以供之后系统使用。

  2. process_kernel_cmdline()
    作用

    读取内核启动参数(存放在/proc/cmdline)。

    解析参数,将符合特定条件的键值对(比如androidboot.android_dt_dir)传递给回调(如init_android_dt_dir()),用于动态配置系统参数(比如设备树路径)。
    主要目的

    根据内核参数,动态调整系统配置(如设备树路径、硬件信息等)。

  3. export_kernel_boot_props()
    作用

    从系统属性(如ro.boot.serialno等)读取硬件和启动参数信息。

    将这些信息导出到常规属性(如ro.serialno、ro.bootmode),方便系统和应用访问。
    主要目的

    将硬件和启动信息显式存入标准化的系统属性,保证系统和应用可以一致地访问硬件配置信息。

相关推荐
叽哥1 小时前
flutter学习第 5 节:文本与样式
android·flutter·ios
鹏多多.2 小时前
flutter-使用AnimatedDefaultTextStyle实现文本动画
android·前端·css·flutter·ios·html5·web
0wioiw03 小时前
Android-Kotlin基础(Jetpack①-ViewModel)
android
用户2018792831674 小时前
限定参数范围的注解之 "咖啡店定价" 的故事
android·java
xzkyd outpaper4 小时前
Android中视图测量、布局、绘制过程
android
泓博4 小时前
Android底部导航栏图标变黑色
android
包达叔4 小时前
使用 Tauri 开发 Android 应用:环境搭建与入门指南
android
初学者-Study4 小时前
Android UI(一)登录注册
android·ui
视觉CG5 小时前
【JS】扁平树数据转为树结构
android·java·javascript