Android 11属性系统初始化流程

这篇具有很好参考价值的文章主要介绍了Android 11属性系统初始化流程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在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
Android 11属性系统初始化流程,Android属性系统,android,属性系统文章来源地址https://www.toymoban.com/news/detail-844884.html

到了这里,关于Android 11属性系统初始化流程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring 填充属性和初始化流程源码剖析及扩展实现

    在上一篇博文 讲解 Spring 实例化的不同方式及相关生命周期源码剖析 介绍了 Spring 实例化的不同方式,本文主要围绕实例化过后对象的填充属性和初始化过程进行详细流程剖析 回顾前言知识,doCreateBean-createBeanInstance,通过 Supplier 接口、FactoryMethod、构造函数反射 invoke,创建

    2024年02月06日
    浏览(44)
  • Android 9 蓝牙协议初始化

    先讲一下Application类的使用 要使用自定义的Application,首先就是要自己新建一个Application的子类,然后把它的名字写在manifest文件里面的application标签里的android:name属性就行,如我的Application子类名字是BaseApplication,则: 1. 初始化资源 由于Application类是在APP启动的时候就启动,

    2024年02月11日
    浏览(50)
  • android系列-init 初始化日志

    1.FirstStageMain 2.InitKernelLogging  3.KernelLogger  4.OpenKmsg 

    2024年02月02日
    浏览(80)
  • Android12 P2P 初始化

    目录  学习资料:Android Miracast 投屏 1. 开机log 2. WifiP2pService 启动

    2024年02月06日
    浏览(50)
  • Android OpenGL 教程——窗口初始化&绘制矩形

    上节介绍了 Native 工程初始化,但是我们的窗口什么都没有,这节我们将 NativeActivity 创建的 window 绑定到 OpenGL 创建的 display 的 surface 上,并且绘制背景颜色和三角形,通过三角形绘制出一个矩形 初始化 绘制  只需要将三角形的索引改为6个即可 绘制 源码:OpenGLDemo#triangle 你

    2024年02月16日
    浏览(43)
  • Android OpenGL 教程——Native 工程初始化

    NDK 的适用场景官方给出三点: 平台间的 App 移植 复用现有库 对软件性能要求较高的场合比如游戏等 有两种方式可以实现 native activity。 native_activity.h android_native_app_glue 由于第二种方法启用另一个线程处理回调和输入事件,NDK 的例子中就采用了这个实现方式。       NDK 工程

    2024年02月16日
    浏览(38)
  • Android性能优化之游戏引擎初始化ANR

    近期,着手对bugly上的anr 处理,记录下优化的方向。 借用网上的一张图: 这里的anr 问题是属于主线程的call 耗时操作。需要使用trace 来获取发生anr前一些列的耗时方法调用时间,再次梳理业务,才可能解决。 问题1 java 调用栈: 从调用栈中发现onActivityResult()执行对游戏侧的

    2024年02月15日
    浏览(50)
  • android存储3--初始化.unlock事件的处理

    android版本:android-11.0.0_r21 http://aospxref.com/android-11.0.0_r21 概述:SystemServiceManager收到unlock事件后,遍历service链表,执行各个service的onUserUnlocking。对于存储service,执行的是StorageManagerService$Lifecycle中的 onUserUnlocking,在这个方法中,存储的 StorageSessionController、vold、storaged模块进行

    2024年02月10日
    浏览(52)
  • android存储4--初始化.emulated设备的挂载

    android版本:android-11.0.0_r21 http://aospxref.com/android-11.0.0_r21 android手机的挂载非常复杂。这篇文章针对emulated存储,介绍它的挂载过程。 android早期,手机内部flash容量比较小,为了能让手机存放更多的文件,需要通过外部存储(如SD卡)来扩展存储容量。随着技术的进步,大容量

    2024年02月15日
    浏览(111)
  • Android---Class 对象在执行引擎中的初始化过程

    一个 class 文件被加载到内存中的步骤如下图所示: 装载  装载是指 Java 虚拟机查找 .class 文件并生成 字节流 ,然后根据字节流创建 java.lang.Class 对象的过程。 1. ClassLoader 通过一个类的 全限定名 ( 包名+类名 )来查找 .class 文件,并生成二进制字节流。其中 class 字节码文件

    2024年02月07日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包