Android 11属性系统初始化流程

news2025/2/24 8:19:52

在init进程启动的第二阶段,调用PropertyInit 对属性系统进行初始化

int SecondStageMain(int argc, char** argv) {
	//省略
	PropertyInit();
	//省略
}

PropertyInit函数在system\core\init\property_service.cpp 中实现

void PropertyInit() {
   //省略
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); //1
    CreateSerializedPropertyInfo();//2
    if (__system_property_area_init()) {//3
        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(); //4
    ProcessKernelCmdline();//5

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    ExportKernelBootProps();//6

    PropertyLoadBootDefaults();//7
}

注释1处在dev下创建__properties__文件夹。注释2处会收集读取各个分区下的property_contexts文件,将读取到的信息系列化之后,写到/dev/properties/property_info文件中。注释3处会读取/dev/properties/property_info文件构建ContextNode 数组,并在/dev/__properties__目录下,创建属性文件.注释4处处理内核dts中的属性信息。注释5处理cmdline中的属性信息。注释6导出一些属性给另外的属性赋值。注释7加载系统默认的属性文件。

接下来一项一项的来分析

CreateSerializedPropertyInfo

void CreateSerializedPropertyInfo() {
    auto property_infos = std::vector<PropertyInfoEntry>();
    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
                                      &property_infos)) {
            return;
        }
        // Don't check for failure here, so we always have a sane list of properties.
        // E.g. In case of recovery, the vendor partition will not have mounted and we
        // still need the system / platform properties to function.
        if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                     &property_infos);
        }
        if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
                                      &property_infos)) {
            // Fallback to nonplat_* if vendor_* doesn't exist.
            LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                     &property_infos);
        }
        if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
                                     &property_infos);
        }
        if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
        }
    } 
    //省略
    auto serialized_contexts = std::string();
    auto error = std::string();
    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
                   &error)) {
        LOG(ERROR) << "Unable to serialize property contexts: " << error;
        return;
    }

    constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
    if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
        PLOG(ERROR) << "Unable to write serialized property infos to file";
    }
    selinux_android_restorecon(kPropertyInfosPath, 0);
  • 初始化property_infos 动态数组
  • 调用LoadPropertyInfoFromFile分别读取各目录下的property_contexts文件,将读取到的信息构建PropertyInfoEntry并放入property_infos 数组
  • BuildTrie对数组进行系列化(构建字典树并对字典树进行TrieBuilerNode 对象系列化),
  • 将系列化后的信息通过WriteStringToFile写进/dev/properties/property_info文件

__system_property_area_init
__system_property_area_init的实现在bionic\libc\bionic\system_property_api.cpp文件中

//#define PROP_FILENAME "/dev/__properties__"
int __system_property_area_init() {
  bool fsetxattr_failed = false;
  return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}

AreaInit函数实现在bionic\libc\system_properties\system_properties.cpp文件中

bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
  if (strlen(filename) >= PROP_FILENAME_MAX) {
    return false;
  }
  strcpy(property_filename_, filename);

  contexts_ = new (contexts_data_) ContextsSerialized();
  if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
    return false;
  }
  initialized_ = true;
  return true;
}

先初始化一个ContextsSerialized对象,然后调用其Initialize函数。注意第一个参数。为true时,则会创建所有的/dev/properties/property_info/u:object_r:*:s0属性安全上下文文件,并在mmap时具有可读写权限。为false时,则不会创建文件且在映射时,只有可读的权限。
接着看一下Initialize函数,实现在bionic\libc\system_properties\contexts_serialized.cpp文件中

bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
  filename_ = filename;
  if (!InitializeProperties()) {
    return false;
  }

  if (writable) {
    mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
    bool open_failed = false;
    if (fsetxattr_failed) {
      *fsetxattr_failed = false;
    }

    for (size_t i = 0; i < num_context_nodes_; ++i) {
      if (!context_nodes_[i].Open(true, fsetxattr_failed)) {
        open_failed = true;
      }
    }
    if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed)) { //映射具有读写权限
      FreeAndUnmap();
      return false;
    }
  } else {
    if (!MapSerialPropertyArea(false, nullptr)) { //映射只有读的权限
      FreeAndUnmap();
      return false;
    }
  }
  return true;
}

在InitializeProperties函数中,会加载/dev/properties/property_info文件,并映射一块内存,然后通过一个 for 循环,将这块内存初始化为一个 ContextNode 的数组结构,每个 ContextNode 对象中的信息保存了一个安全上下文信息和文件路径信息。
完成上面的工作之后,通过for循环,调用每个ContextNode 的Open函数,在Open函数中,会根据属性安全上下文创建属性文件,如dev/properties/u:object_r:hwservicemanager_prop:s0,并映射它,将映射的地址保存在ContextNode 中。需要说明的是,相同安全上下文的不同属性,都会存放在同一片共享内存中,内存的名字就是其安全上下文(比如属性的安全上下文名字为adbd_config_prop,则存放在dev/properties/u:object_r:adbd_config_prop:s0文件中)。

ls -lh dev/__properties__/
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:adbd_config_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:adbd_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:apexd_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:apk_verity_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:audio_prop:s0

ProcessKernelDt

static void ProcessKernelDt() {
    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(), ',', '.');

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

首先检查compatible属性的值是"android,firmware"如果不是,函数直接返回。如果是的话,再打开目录下的dts文件,并读取其内容。其中 get_android_dt_dir是在cmdline中指定

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
    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
        if (key == "androidboot.android_dt_dir") {
            android_dt_dir = value;
        }
    });
    LOG(INFO) << "Using Android DT directory " << android_dt_dir;
    return android_dt_dir;
}

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

比如androidboot.android_dt_dir目录下有cpu.dts和dispaly.dts,满足条件的话,则会将两个文件的内容分别设置ro.boot.cpu和ro.boot.dispaly

ProcessKernelCmdline

static void ProcessKernelCmdline() {
    bool for_emulator = false;
    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
        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);
        });
    }
}

解析cmdline中的数据设置其属性。如:androidboot.mode=normal 会转化成:ro.boot.mode=normal

ExportKernelBootProps

static void ExportKernelBootProps() {
    constexpr const char* UNSET = "";
    struct {
        const char* src_prop;
        const char* dst_prop;
        const char* default_value;
    } prop_map[] = {
            // clang-format off
        { "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", },
            // clang-format on
    };
    for (const auto& prop : prop_map) {
        std::string value = GetProperty(prop.src_prop, prop.default_value);
        if (value != UNSET) InitPropertySet(prop.dst_prop, value);
    }
}

如果系统中有ro.boot.serialno这个属性,则将其值也设置给ro.serialno
PropertyLoadBootDefaults

加载系统默认的属性文件。参考初识Android 属性

总结

在初始化属性系统时,会加载各个分区的property_contexts文件,并进行TrieBuilerNode 对象系列化,然后将系列化的信息写入到dev/properties/propert_info文件中。根据该文件信息(name,安全上下文),在dev/properties/目录下创建内存文件,并进行映射,地址保存在ContextNode 中。创建的内存文件的名字是属性的安全上下文。ContextNode 和TrieBuilerNode 是一一对应的。内存创建好之后,还会根据dts,cmdline中的信息,生成属性。最后加载各个分区默认的属性文件。

以下为简略的逻辑图,每个内存文件为128k
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1576263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《云原生安全攻防》-- 云原生应用风险分析

为了满足每位朋友的学习需求&#xff0c;并且支持课程的持续更新&#xff0c;本系列课程提供了免费版和付费视频版两种方式来提供课程内容。我们会持续更新课程内容&#xff0c;以确保内容的度和实用性。 在本节课程中&#xff0c;我们将一起探讨云原生应用在新的架构模式下可能…

模块化——如何导入模块?(内置模块与自定义模块)

在Node.js中&#xff0c;要导入另一个模块&#xff0c;我们可以使用require函数。这个函数接受一个文件路径参数&#xff0c;并返回导入的模块。 一、require使用注意事项&#xff1a; (1)自己创建的模块&#xff0c;导入时建议写相对路径&#xff0c;不能省略./和../ //我把…

Struts2:Action类的写法,推荐使用继承ActionSupport类的方法

文章目录 方法一&#xff1a;Action类是一个POJO类&#xff08;简单的Java类&#xff09;ActionDemo2.javastruts_demo2.xmlstruts.xml运行结果其他strutsz_demo1.xml 方法二&#xff1a;实现一个Action的接口ActionDemo2_2.javastruts_demo2.xml运行结果 推荐&#xff01;&…

基于视频监管与AI智能识别技术的水利河道综合治理解决方案

一、方案介绍 TSINGSEE青犀视频水利河道综合治理解决方案是依托视频AI智能分析技术&#xff0c;利用水质/水文等传感器、高清摄像机、水利球、无人机、无人船等感知设备实时采集数据&#xff0c;并与视频能力进行联动&#xff0c;达到智能预警的目的。 TSINGSEE青犀方案以信息…

【单源最短路 图论】882. 细分图中的可到达节点

作者推荐 视频算法专题 本文涉及知识点 单源最短路 图论 LeetCode 882. 细分图中的可到达节点 给你一个无向图&#xff08;原始图&#xff09;&#xff0c;图中有 n 个节点&#xff0c;编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链&#xff0c;每条边之间…

4月7号总结

java学习 一.正则表达式 定义&#xff1a;正则表达式是一种用于描述字符串模式的表达式&#xff0c;通常被用于文本搜索、匹配和替换。它是一种强大的工具&#xff0c;可以在文本处理和文本分析中进行复杂的匹配和操作。 通过字符串引用里面的方法matches&#xff0c;然后执行…

jupyter python paramiko 网络系统运维

概述 通过使用jupyter进行网络运维的相关测试 设备为H3C 联通性测试 import paramiko import time import getpass import re import os import datetimeusername "*****" password "*****" ip "10.32.**.**"ssh_client paramiko.SSHCli…

相机标定——四个坐标系介绍

世界坐标系(Xw,Yw,Zw) 世界坐标系是一个用于描述和定位三维空间中物体位置的坐标系&#xff0c;通常反映真实世界下物体的位置和方向。它是一个惯性坐标系&#xff0c;被用作整个场景或系统的参考框架。在很多情况下&#xff0c;世界坐标系被认为是固定不变的&#xff0c;即它…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果 一、简单介绍 二、简单视频倒放效果实现原理 三、简单视频倒放效果案例实现…

C++搭建深度学习的推理框架

我们的目的是:借助C++搭建一个类似于pytorch,tensorflow的深度学习框架,对标pytorch,tensorflow实现对应的功能。由于本人能力有限,下面本人将借助C++搭建一个简单的全连接神经网络,并且尝试解释里面的算子定义和计算图构建。 算子定义 回顾pytorch里面搭建的全连接神经网…

探索未来游戏:生成式人工智能AI如何重塑你的游戏世界?

生成式人工智能&#xff08;Generative AI&#xff09;正以前所未有的速度改变着各行各业的运作模式。其中&#xff0c;游戏产业作为科技应用的前沿阵地&#xff0c;正经历着前所未有的变革。本文将探讨生成式人工智能如何重塑游戏产业&#xff0c;以及这一变革背后的深远影响。…

Sora是什么?Sora怎么使用?Sora最新案例视频以及常见问题答疑

Sora 是什么&#xff1f; 2024年2月16日&#xff0c;OpenAI 在其官网上面正式宣布推出文本生成视频的大模型Sora 这样说吧给你一段话&#xff0c; 让你写一篇800字的论文&#xff0c;你的理解很可能都有偏差&#xff0c;那么作为OpenAi要做文生视频到底有多难&#xff0c;下面…

Linux设备深探:桥接硬件与软件的秘密通道

在Linux的世界里&#xff0c;"设备"这个词汇比你想象的要丰富和多彩得多。让我们一起来探索Linux设备的奥秘&#xff0c;理解它们是如何在Linux操作系统中发挥作用的。&#x1f427;✨ 1. 什么是Linux设备&#xff1f; 在Linux中&#xff0c;设备被看作是一种特殊的…

Day01-SHELL自动化编程-变量与特殊变量

Day01-SHELL自动化编程-变量与特殊变量 1.编程概述2.课程内容3.Shell编程语言必知必会4.编程语言分类&#xff08;按照执行方式&#xff09;-了解5.编程环境准备6.Shell脚本执行方式6.1概述6.2详解6.2.1 sh或bash6.2.1 .或source6.2.3 相对或绝对路径6.2.4 使用重定向符号执行脚…

iOS 17.5系统或可识别并禁用未知跟踪器,苹果Find My技术应用越来越合理

苹果公司去年与谷歌合作&#xff0c;宣布将制定新的行业标准来解决人们日益关注的跟踪器隐私问题。苹果计划在即将发布的 iOS 17.5 系统中加入这项提升用户隐私保护的新功能。 科技网站 9to5Mac 在苹果发布的 iOS 17.5 开发者测试版内部代码中发现了这项反跟踪功能的蛛丝马迹…

Spring源码解析-容器基本实现

spring源码解析 整体架构 defaultListableBeanFactory xmlBeanDefinitionReader 创建XmlBeanFactory 对资源文件进行加载–Resource 利用LoadBeandefinitions(resource)方法加载配置中的bean loadBeandefinitions加载步骤 doLoadBeanDefinition xml配置模式 validationMode 获…

海外媒体宣发,穿透与世界的交流 - “保姆级”教程 - 大舍传媒

1. 引言 在当今高度信息化的世界&#xff0c;境外媒体宣发已经成为企业、品牌和政府机构推广自身形象、扩大影响力的重要手段。如何在国际舞台上有效传播信息&#xff0c;提高国际知名度&#xff0c;成为了许多组织面临的重要课题。大舍传媒凭借多年的境外媒体宣发经验&#x…

数据恢复与数据取证的便携工具:PC-3000 Portable III

天津鸿萌科贸发展有限公司从事数据安全业务20余年&#xff0c;在数据恢复、数据取证、数据备份等领域有丰富的案例经验、前沿专业技术及良好的行业口碑。同时&#xff0c;公司面向取证机构及数据恢复同行&#xff0c;提供实验室建设方案&#xff0c;包含数据恢复与数据取证硬件…

elementPlus el-table动态列扩展及二维表格

1、循环列数据源&#xff0c;动态生成列 <template><div><el-table ref"table" :data"pageData.tableData" stripe style"width: 100%"><el-table-column v-for"column in pageData.columns" :key"column.p…

SMATool 程序:VASP + QE + 零温 + 有限温度 + 拉伸、剪切、双轴、维氏硬度的计算

分享一个 SMATool 程序&#xff1a;用于评估材料在零温&#xff08;使用密度泛函理论&#xff09;和有限温度&#xff08;使用从头算分子动力学&#xff09;下的拉伸、剪切、双轴和压痕强度&#xff08;维氏硬度&#xff09;。 感谢论文的原作者&#xff01; 关键词&#xff…