Android 12 init 属性服务

Posted pecuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 init 属性服务相关的知识,希望对你有一定的参考价值。

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

在 init 的启动第二阶段,启动属性服务线程,提供相关属性服务,给其他进程提供设置属性的支持,并通知init去处理属性事件。

SecondStageMain

/// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) 
  ...
  PropertyInit(); // 属性环境相关初始化,及固有属性加载
  ...
  StartPropertyService(&property_fd); // 启动属性服务
  ...

PropertyInit

/// @system/core/init/property_service.cpp
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); // 创建/dev/__properties__目录,权限drwx--x--x
    CreateSerializedPropertyInfo(); // 创建property se contexts
    if (__system_property_area_init())  // 将 /dev/__properties__/properties_serial 映射到内存, 创建 ContextNodes,映射节点
        LOG(FATAL) << "Failed to initialize property area";
    
    if (!property_info_area.LoadDefaultPath())  // 加载/dev/__properties__/property_info 映射进内存
        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(); // 解析 /proc/device-tree/firmware/android/
    ProcessKernelCmdline(); // 解析 /proc/cmdline , 将其中键值对满足key为androidboot.* 的,将key替换为ro.boot.*,然后添加到属性
    ProcessBootconfig(); // 解析 /proc/bootconfig ,同上

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    ExportKernelBootProps(); // 给一些kernel属性初始赋值,如果没有设置的话

    // 加载默认的属性文件的属性
    // 如 /system/build.prop /vendor/default.prop /vendor/build.prop
    // 还会解析其他分区 如 odm、product、system_ext
    PropertyLoadBootDefaults();

CreateSerializedPropertyInfo

从相关property_contexts文件读取到context信息,并将内容序列化,然后写入 /dev/properties/property_info

void CreateSerializedPropertyInfo() 
    auto property_infos = std::vector<PropertyInfoEntry>();
    // 从 selinux 的相关 property context 解析属性上下文信息,并存入 property_infos
    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, since we don't always have all of these partitions.
        // 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);
        
     else 
        if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) 
            return;
        
        LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);
        if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) 
            // Fallback to nonplat_* if vendor_* doesn't exist.
            LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
        
        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/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)) // 写入property_info
        PLOG(ERROR) << "Unable to write serialized property infos to file";
    
    selinux_android_restorecon(kPropertyInfosPath, 0);

PropertyInfoEntry 定义如下,它用来存储解析后的context信息

/// @system/core/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
struct PropertyInfoEntry 
  PropertyInfoEntry() 
  template <typename T, typename U, typename V>
  PropertyInfoEntry(T&& name, U&& context, V&& type, bool exact_match)
      : name(std::forward<T>(name)),
        context(std::forward<U>(context)),
        type(std::forward<V>(type)),
        exact_match(exact_match) 
  std::string name;
  std::string context;
  std::string type;
  bool exact_match;
;

查看 /system/etc/selinux/plat_property_contexts 其中一行,对应关系如下

// name                context                            exact_match   type
sys.shutdown.requested u:object_r:exported_system_prop:s0 exact         string

__system_property_area_init

将 /dev/properties/properties_serial 映射到内存, 创建 ContextNodes 流程

#define PROP_FILENAME "/dev/__properties__"
/// @bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_area_init() 
  bool fsetxattr_failed = false;
  return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;

SystemProperties::AreaInit

创建 ContextsSerialized 并初始化

/// @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); // filename 是 /dev/__properties__

  contexts_ = new (contexts_data_) ContextsSerialized();
  // 初始化 ContextsSerialized
  if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) 
    return false;
  
  initialized_ = true;
  return true;

ContextsSerialized::Initialize

第一个参数 writable 为 true, filename 为 /dev/properties

/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) 
  filename_ = filename;  // 此处传入的是 /dev/__properties__
  if (!InitializeProperties())  // 初始化属性
    return false;
  

  if (writable) 
    mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
    bool open_failed = false;
    if (fsetxattr_failed) 
      *fsetxattr_failed = false;
    
    // 遍历 ContextNode 数组,将各个节点映射到内存
    for (size_t i = 0; i < num_context_nodes_; ++i) 
      if (!context_nodes_[i].Open(true, fsetxattr_failed))  // 打开各个节点,并映射到内存,注意此处的true 表示access_rw
        open_failed = true;
      
    
    if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed))  // /dev/__properties__/properties_serial 映射到内存
      FreeAndUnmap();
      return false;
    
   else 
    if (!MapSerialPropertyArea(false, nullptr)) 
      FreeAndUnmap();
      return false;
    
  
  return true;

InitializeProperties

/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::InitializeProperties() 
  if (!property_info_area_file_.LoadDefaultPath())  // 加载 property_info
    return false;
  

  if (!InitializeContextNodes())  // 创建 context 节点集合
    FreeAndUnmap();
    return false;
  

  return true;


/// 加载 /dev/__properties__/property_info
bool PropertyInfoAreaFile::LoadDefaultPath() 
  return LoadPath("/dev/__properties__/property_info");


bool PropertyInfoAreaFile::LoadPath(const char* filename) 
  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);

  struct stat fd_stat;
  if (fstat(fd, &fd_stat) < 0)  // 获取fstat
    close(fd);
    return false;
  

  if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
      ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
      (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) 
    close(fd);
    return false;
  

  auto mmap_size = fd_stat.st_size;
  // 映射到内存
  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
  if (map_result == MAP_FAILED) 
    close(fd);
    return false;
  
  // 转为具体类型
  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
  if (property_info_area->minimum_supported_version() > 1 ||
      property_info_area->size() != mmap_size) 
    munmap(map_result, mmap_size);
    close(fd);
    return false;
  

  close(fd);
  mmap_base_ = map_result; // 记录地址和大小
  mmap_size_ = mmap_size;
  return true;

InitializeContextNodes

根据context信息创建对应的 ContextNode

/// @bionic/libc/system_properties/include/system_properties/contexts_serialized.h
android::properties::PropertyInfoAreaFile property_info_area_file_;// property info 映射文件
ContextNode* context_nodes_ = nullptr;
size_t num_context_nodes_ = 0;
size_t context_nodes_mmap_size_ = 0;

/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::InitializeContextNodes() 
  auto num_context_nodes = property_info_area_file_->num_contexts();
  auto context_nodes_mmap_size = sizeof(ContextNode) * num_context_nodes;
  // We want to avoid malloc in system properties, so we take an anonymous map instead (b/31659220).
  void* const map_result = mmap(nullptr, context_nodes_mmap_size, PROT_READ | PROT_WRITE,
                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // 映射一个所有节点总大小空间
  if (map_result == MAP_FAILED) 
    return false;
  

  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_result, context_nodes_mmap_size,
        "System property context nodes");

  context_nodes_ = reinterpret_cast<ContextNode*>(map_result);
  num_context_nodes_ = num_context_nodes;
  context_nodes_mmap_size_ = context_nodes_mmap_size;
  // 根据每个context创建对应的 ContextNode,并存入 context_nodes_,filename_ 是Initialize函数传入的 /dev/__properties__
  for (size_t i = 0; i < num_context_nodes; ++i) 
    new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), filename_);
  

  return true;

创建每个节点后,接下来是调用它的Open函数

ContextNode::Open

从上面可以知道,init中 access_rw 传入的是 true

/// @bionic/libc/system_properties/context_node.cpp
bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed) 
  lock_.lock();
  if (pa_) 
    lock_.unlock();
    return true;
  

  char filename[PROP_FILENAME_MAX];
  // 此处构建的是 filename_/context_ , 比如 /dev/__properties__/u:object_r:vold_prop:s0
  int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
  if (len < 0 || len >= PROP_FILENAME_MAX) 
    lock_.unlock();
    return false;
  

  if (access_rw)  // 上面传入的是access_rw=true,调用map_prop_area_rw
    pa_ = prop_area::map_prop_area_rw(filename, context_, fsetxattr_failed); // 映射并记录地址,设置context
   else 
    pa_ = prop_area::map_prop_area(filename);
  
  lock_.unlock();
  return pa_;

map_prop_area_rw 函数打开相关文件,然后将其映射到内存,返回内存地址指针。

/// @bionic/libc/system_properties/prop_area.cpp
prop_area* prop_area::map_prop_area_rw(const char* filename, const char* context,
                                       bool* fsetxattr_failed) 
  /* dev is a tmpfs that we can use to carve a shared workspace
   * out of, so let's do that...
   */  // 注意此处rwx权限是 444 只读, 因此只有 init 才有权限去写
  const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);

  if (fd < 0) 
    if (errno == EACCES) 
      /* for consistency with the case where the process has already
       * mapped the page in and segfaults when trying to write to it
       */
      abort();
    
    return nullptr;
  

  if (context)  // 设置文件的context,比如 u:object_r:vold_prop:s0
    if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) 
      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                            "fsetxattr failed to set context (%s) for \\"%s\\"", context, filename);
      /*
       * fsetxattr() will fail during system properties tests due to selinux policy.
       * We do not want to create a custom policy for the tester, so we will continue in
       * this function but set a flag that an error has occurred.
       * Init, which is the only daemon that should ever call this function will abort
       * when this error occurs.
       * Otherwise, the tester will ignore it and continue, albeit without any selinux
       * property separation.
       */
      if (fsetxattr_failed) 
        *fsetxattr_failed = true;
      
    
  

  if (ftruncate(fd, PA_SIZE) < 0) 
    close(fd);
    return nullptr;
  

  pa_size_ = PA_SIZE;
  pa_data_size_ = pa_size_ - sizeof(prop_area);
  // 映射到内存,具有读写权限
  void* const memory_area = mmap(nullptr, pa_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (memory_area == MAP_FAILED) 
    close(fd);
    return nullptr;
  

  prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);

  close(fd);
  return pa;

MapSerialPropertyArea

最后将 /dev/__properties__/properties_serial 映射到内存,作为存储 context为 u:object_r:properties_serial:s0 的属性的文件

/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed) 
  char filename[PROP_FILENAME_MAX];
  int len = async_safe_format_buffer(filename, sizeof(filename), "%s/properties_serial", filename_);
 

以上是关于Android 12 init 属性服务的主要内容,如果未能解决你的问题,请参考以下文章

android 启动流程 相关2 init进程 属性服务

Android 12 init 子进程回收与服务重启分析

Android 12 init 子进程回收与服务重启分析

Android WebView全屏视频退出创建额外的空白区域

Android源码下载repo以及repo init总结

Android源码下载repo以及repo init总结