Android 12 init(3) 属性服务

文章托管在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_);
  if (len < 0 || len >= PROP_FILENAME_MAX) {
    serial_prop_area_ = nullptr;
    return false;
  }

  if (access_rw) {
    serial_prop_area_ =  // 映射到内存,并设置context为 u:object_r:properties_serial:s0
        prop_area::map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
  } else {
    serial_prop_area_ = prop_area::map_prop_area(filename);
  }
  return serial_prop_area_;
}

总结一下上面__system_property_area_init()流程:

  • 创建/dev/__properties__目录,权限drwx–x–x
  • 解析property context文件并将信息存储到 /dev/__properties__/property_info
  • 映射property_info,读取property context并根据每个context创建对应的 ContextNode
  • ContextNode::Open 创建节点文件并以读写映射到内存
  • 将/dev/__properties__/properties_serial 映射到内存

到此 __system_property_area_init() 完成,创建了所有context对应的节点并映射到内存以及映射properties_serial,可以查看/dev/__properties__

# ls /dev/__properties__/ -lZ
total 1136
-r--r--r-- 1 root root u:object_r:properties_serial:s0                                131072 2022-11-30 14:24 properties_serial
-r--r--r-- 1 root root u:object_r:property_info:s0                                     64956 2022-11-30 14:24 property_info
-r--r--r-- 1 root root u:object_r:aac_drc_prop:s0                                     131072 2022-11-30 14:24 u:object_r:aac_drc_prop:s0
-r--r--r-- 1 root root u:object_r:aaudio_config_prop:s0                               131072 2022-11-30 14:24 u:object_r:aaudio_config_prop:s0
-r--r--r-- 1 root root u:object_r:ab_update_gki_prop:s0                               131072 2022-11-30 14:24 u:object_r:ab_update_gki_prop:s0
-r--r--r-- 1 root root u:object_r:adbd_config_prop:s0                                 131072 2022-11-30 14:24 u:object_r:adbd_config_prop:s0
-r--r--r-- 1 root root u:object_r:adbd_prop:s0                                        131072 2022-11-30 14:24 u:object_r:adbd_prop:s0
-r--r--r-- 1 root root u:object_r:apexd_config_prop:s0                                131072 2022-11-30 14:24 u:object_r:apexd_config_prop:s0
-r--r--r-- 1 root root u:object_r:apexd_prop:s0                                       131072 2022-11-30 14:24 u:object_r:apexd_prop:s0
...

property_info_area.LoadDefaultPath

加载 /dev/__properties__/property_info 并映射到内存,逻辑同之前 property_info_area_file_ 加载

bool PropertyInfoAreaFile::LoadDefaultPath() {
  return LoadPath("/dev/__properties__/property_info");
}

ProcessKernelCmdline / ProcessBootconfig

/// @system/core/init/property_service.cpp
constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;

static void ProcessKernelCmdline() {
    // 加载 /proc/cmdline
    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
        if (StartsWith(key, ANDROIDBOOT_PREFIX)) { // key 起始是 androidboot
            // 将key起始位置替换为ro.boot, 然后设置新属性
            InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
        }
    });
}

static void ProcessBootconfig() {
    // 加载 /proc/bootconfig
    ImportBootconfig([&](const std::string& key, const std::string& value) {
        if (StartsWith(key, ANDROIDBOOT_PREFIX)) { // key 起始是 androidboot
            // 将key起始位置替换为ro.boot, 然后设置新属性
            InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
        }
    });
}

// 读取/proc/cmdline , 匹配回调 fn
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]);
        }
    }
}

// 读取/proc/bootconfig , 匹配回调 fn
void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
    std::string bootconfig;
    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);

    for (const auto& entry : android::base::Split(bootconfig, "\n")) {
        std::vector<std::string> pieces = android::base::Split(entry, "=");
        if (pieces.size() == 2) {
            // get rid of the extra space between a list of values and remove the quotes.
            std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
            value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
            fn(android::base::Trim(pieces[0]), android::base::Trim(value));
        }
    }
}

PropertyLoadBootDefaults

加载开机默认属性

/// @system/core/init/property_service.cpp
void PropertyLoadBootDefaults() {
    // We read the properties and their values into a map, in order to always allow properties
    // loaded in the later property files to override the properties in loaded in the earlier
    // property files, regardless of if they are "ro." properties or not.
    std::map<std::string, std::string> properties;

    if (IsRecoveryMode()) {
        load_properties_from_file("/prop.default", nullptr, &properties);
    }

    // //etc/build.prop is the canonical location of the build-time properties since S.
    // Falling back to //defalt.prop and //build.prop only when legacy path has to
    // be supported, which is controlled by the support_legacy_path_until argument.
    const auto load_properties_from_partition = [&properties](const std::string& partition,
                                                              int support_legacy_path_until) {  // 加载某个分区的lambda表达式
        auto path = "/" + partition + "/etc/build.prop";
        if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
            return;
        }
        // To read ro..build.version.sdk, temporarily load the legacy paths into a
        // separate map. Then by comparing its value with legacy_version, we know that if the
        // partition is old enough so that we need to respect the legacy paths.
        std::map<std::string, std::string> temp;
        auto legacy_path1 = "/" + partition + "/default.prop";
        auto legacy_path2 = "/" + partition + "/build.prop";
        load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);
        load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);
        bool support_legacy_path = false;
        auto version_prop_name = "ro." + partition + ".build.version.sdk";
        auto it = temp.find(version_prop_name);
        if (it == temp.end()) {
            // This is embarassing. Without the prop, we can't determine how old the partition is.
            // Let's be conservative by assuming it is very very old.
            support_legacy_path = true;
        } else if (int value;
                   ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {
            support_legacy_path = true;
        }
        if (support_legacy_path) {
            // We don't update temp into properties directly as it might skip any (future) logic
            // for resolving duplicates implemented in load_properties_from_file.  Instead, read
            // the files again into the properties map.
            load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);
            load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);
        } else {
            LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "
                       << "because " << version_prop_name << "(" << it->second << ") is newer "
                       << "than " << support_legacy_path_until;
        }
    };

    // Order matters here. The more the partition is specific to a product, the higher its
    // precedence is.
    LoadPropertiesFromSecondStageRes(&properties);
    load_properties_from_file("/system/build.prop", nullptr, &properties); // 加载某个文件
    load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);  // 加载某个分区的
    // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
    // all updated.
    // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    // }
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
    load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
    load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
    load_properties_from_partition("product", /* support_legacy_path_until */ 30);

    if (access(kDebugRamdiskProp, R_OK) == 0) {
        LOG(INFO) << "Loading " << kDebugRamdiskProp;
        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
    }

    for (const auto& [name, value] : properties) { // 将加载的属性键值对执行设置操作,保持到映射区
        std::string error;
        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
            LOG(ERROR) << "Could not set '" << name << "' to '" << value
                       << "' while loading .prop files" << error;
        }
    }
    // 启动属性初始化
    property_initialize_ro_product_props();
    property_initialize_build_id();
    property_derive_build_fingerprint();
    property_derive_legacy_build_fingerprint();
    property_initialize_ro_cpu_abilist();

    update_sys_usb_config();  // 更新usb配置属性
}

update_sys_usb_config

// persist.sys.usb.config values can't be combined on build-time when property
// files are split into each partition.
// So we need to apply the same rule of build/make/tools/post_process_props.py
// on runtime.
static void update_sys_usb_config() {
    bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
    std::string config = android::base::GetProperty("persist.sys.usb.config", "");
    // b/150130503, add (config == "none") condition here to prevent appending
    // ",adb" if "none" is explicitly defined in default prop.
    if (config.empty() || config == "none") {
        InitPropertySet("persist.sys.usb.config", is_debuggable ? "adb" : "none");
    } else if (is_debuggable && config.find("adb") == std::string::npos &&
               config.length() + 4 < PROP_VALUE_MAX) {
        config.append(",adb");
        InitPropertySet("persist.sys.usb.config", config);
    }
}

StartPropertyService

启动属性服务

/// @system/core/init/property_service.cpp
void StartPropertyService(int* epoll_socket) {
    InitPropertySet("ro.property_service.version", "2");

    int sockets[2];
    // 创建 socket 对,用于init和属性服务间通信
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
    }
    *epoll_socket = from_init_socket = sockets[0]; // 回传给init端
    init_socket = sockets[1]; // 持有此fd端
    StartSendingMessages(); // 设置 accept_messages = true , 表示可以处理请求消息

    // 创建接收属性请求的 socket
    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, {});
        result.ok()) {
        property_set_fd = *result; // 记录此fd
    } else {
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    listen(property_set_fd, 8); // 监听

    auto new_thread = std::thread{PropertyServiceThread}; // 创建新线程,调用PropertyServiceThread用于处理请求,
    property_service_thread.swap(new_thread);
}

PropertyServiceThread

/// @system/core/init/property_service.cpp
static void PropertyServiceThread() {
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) { // 创建 epoll
        LOG(FATAL) << result.error();
    }
    // 将属性设置fd注册到epoll,监听其相关事件
    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
        !result.ok()) {
        LOG(FATAL) << result.error();
    }
    // 注册init_socket到epoll,监听来自init相关事件
    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
        LOG(FATAL) << result.error();
    }

    while (true) { // 循环处理事件
        auto pending_functions = epoll.Wait(std::nullopt); // 等待事件发生
        if (!pending_functions.ok()) {
            LOG(ERROR) << pending_functions.error();
        } else {
            for (const auto& function : *pending_functions) {
                (*function)();
            }
        }
    }
}

当收到设置属性请求时,会回调 handle_property_set_fd 函数

handle_property_set_fd

/// @system/core/init/property_service.cpp
static void handle_property_set_fd() {
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    if (s == -1) {
        return;
    }

    ucred cr;
    socklen_t cr_size = sizeof(cr);
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
        close(s);
        PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
        return;
    }

    SocketConnection socket(s, cr);
    uint32_t timeout_ms = kDefaultSocketTimeout;

    uint32_t cmd = 0;
    if (!socket.RecvUint32(&cmd, &timeout_ms)) {
        PLOG(ERROR) << "sys_prop: error while reading command from the socket";
        socket.SendUint32(PROP_ERROR_READ_CMD);
        return;
    }

    switch (cmd) {
    case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];

        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }

        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;

        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
            return;
        }

        const auto& cr = socket.cred();
        std::string error;
        uint32_t result =
                HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }

        break;
      }

    case PROP_MSG_SETPROP2: {
        std::string name;
        std::string value;
        if (!socket.RecvString(&name, &timeout_ms) ||
            !socket.RecvString(&value, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
          socket.SendUint32(PROP_ERROR_READ_DATA);
          return;
        }

        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
            return;
        }

        const auto& cr = socket.cred();
        std::string error; // 调用 HandlePropertySet 设置属性
        uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }
        socket.SendUint32(result);
        break;
      }

    default:
        LOG(ERROR) << "sys_prop: invalid command " << cmd;
        socket.SendUint32(PROP_ERROR_INVALID_CMD);
        break;
    }
}

HandlePropertySet

处理设置属性

/// @system/core/init/property_service.cpp
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr,
                           SocketConnection* socket, std::string* error) {
    // 权限检查                         
    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
        return ret;
    }

    if (StartsWith(name, "ctl.")) { // 处理 ctl 属性
        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
    }

    // sys.powerctl is a special property that is used to make the device reboot.  We want to log
    // any process that sets this property to be able to accurately blame the cause of a shutdown.
    if (name == "sys.powerctl") { // 处理关机/重启请求
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid); // 读取请求进程信息
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
            // path.
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                  << process_log_string;
        if (!value.empty()) {
            DebugRebootLogging();
        }
        if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
            *error = "Userspace reboot is not supported by this device";
            return PROP_ERROR_INVALID_VALUE;
        }
    }

    // If a process other than init is writing a non-empty value, it means that process is
    // requesting that init performs a restorecon operation on the path specified by 'value'.
    // We use a thread to do this restorecon operation to prevent holding up init, as it may take
    // a long time to complete.
    if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
        static AsyncRestorecon async_restorecon;
        async_restorecon.TriggerRestorecon(value);
        return PROP_SUCCESS;
    }

    return PropertySet(name, value, error); // 真正设置更新
}

PropertySet

/// @system/core/init/property_service.cpp
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
    size_t valuelen = value.size();

    if (!IsLegalPropertyName(name)) { // 值合法性检查
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }

    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {  // 值合法性检查
        *error = result.error().message();
        return PROP_ERROR_INVALID_VALUE;
    }

    prop_info* pi = (prop_info*) __system_property_find(name.c_str());  // 查找属性信息是否存在
    if (pi != nullptr) {
        // ro.* properties are actually "write-once".
        if (StartsWith(name, "ro.")) {  // 只读属性不能设置
            *error = "Read-only property was already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }

        __system_property_update(pi, value.c_str(), valuelen); // 更新
    } else {// 添加属性
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
            *error = "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }

    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
        WritePersistentProperty(name, value);  // 写入 persist 属性
    }
    // If init hasn't started its main loop, then it won't be handling property changed messages
    // anyway, so there's no need to try to send them.
    auto lock = std::lock_guard{accept_messages_lock};
    if (accept_messages) {
        PropertyChanged(name, value);  //通知属性变化
    }
    return PROP_SUCCESS;
}

PropertyChanged

属性变化处理逻辑

/// @system/core/init/init.cpp
void PropertyChanged(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
    // commands to be executed.
    if (name == "sys.powerctl") { // 处理关机请求
        trigger_shutdown(value);
    }

    if (property_triggers_enabled) {// 已经使能属性触发器
        ActionManager::GetInstance().QueuePropertyChange(name, value); //添加到事件队列
        WakeMainInitThread(); // 唤醒init主线程处理
    }

    prop_waiter_state.CheckAndResetWait(name, value); // 检查是否有设置等待此属性值
}

ActionManager::QueuePropertyChange

/// @system/core/init/action_manager.cpp
void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
    auto lock = std::lock_guard{event_queue_lock_};
    event_queue_.emplace(std::make_pair(name, value)); // 插入一个属性变化的事件
}

WakeMainInitThread

/// @system/core/init/init.cpp
static void WakeMainInitThread() {
    uint64_t counter = 1; // 写入一个数据,唤醒对端
    TEMP_FAILURE_RETRY(write(wake_main_thread_fd, &counter, sizeof(counter)));
}

// Init epolls various FDs to wait for various inputs.  It previously waited on property changes
// with a blocking socket that contained the information related to the change, however, it was easy
// to fill that socket and deadlock the system.  Now we use locks to handle the property changes
// directly in the property thread, however we still must wake the epoll to inform init that there
// is a change to process, so we use this FD.  It is non-blocking, since we do not care how many
// times WakeMainInitThread() is called, only that the epoll will wake.
static int wake_main_thread_fd = -1;
static void InstallInitNotifier(Epoll* epoll) { // 在启动属性服务之前,调用来设置醒监听
    wake_main_thread_fd = eventfd(0, EFD_CLOEXEC); // 初始化一个event fd
    if (wake_main_thread_fd == -1) {
        PLOG(FATAL) << "Failed to create eventfd for waking init";
    }
    auto clear_eventfd = [] { // epoll收到相关事件会回调此lambda
        uint64_t counter; // 仅仅只是读出来,消费掉事件。主要目的是唤醒epoll
        TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));
    };
    // 注册到epoll 监听写事件
    if (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) {
        LOG(FATAL) << result.error();
    }
}

SecondStageMain 处理属性事件

int SecondStageMain(int argc, char** argv) {
...
// Restore prio before main loop
setpriority(PRIO_PROCESS, 0, 0);
while (true) {
    // By default, sleep until something happens.
    auto epoll_timeout = std::optional<std::chrono::milliseconds>{};

    auto shutdown_command = shutdown_state.CheckShutdown();
    if (shutdown_command) { // 处理关机请求
        LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                  << "' Calling HandlePowerctlMessage()";
        HandlePowerctlMessage(*shutdown_command);
        shutdown_state.set_do_shutdown(false);
    }
    // 当没有要等待的属性或执行的服务 则从事件队列中取出一个执行
    if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
        am.ExecuteOneCommand();
    }
    if (!IsShuttingDown()) { // 不是正在关机, 如有需要重启的服务,需要据此重新计算超时时间
        auto next_process_action_time = HandleProcessActions();

        // If there's a process that needs restarting, wake up in time for that.
        if (next_process_action_time) {
            epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                    *next_process_action_time - boot_clock::now());
            if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
        }
    }

    if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
        // If there's more work to do, wake up again immediately.
        if (am.HasMoreCommands()) epoll_timeout = 0ms; // 还有事件需要处理,超时为0,即里面处理下个事件
    }

    auto pending_functions = epoll.Wait(epoll_timeout); // 等待到新消息到来或超时
    if (!pending_functions.ok()) {
        LOG(ERROR) << pending_functions.error();
    } else if (!pending_functions->empty()) { // 有待执行命令,比如唤醒要执行的回调 clear_eventfd
        // We always reap children before responding to the other pending functions. This is to
        // prevent a race where other daemons see that a service has exited and ask init to
        // start it again via ctl.start before init has reaped it.
        ReapAnyOutstandingChildren(); // 首先回收已退出的进程,回收僵尸进程
        for (const auto& function : *pending_functions) { // 执行相关回调
            (*function)();
        }
    }
    if (!IsShuttingDown()) { // 不是正在关机,
        HandleControlMessages(); // 处理 ctl 属性消息
        SetUsbController();
    }
}

return 0;
}

如上,当属性服务写数据时唤醒epoll,epoll从Wait函数返回,然后处理此事件,回调clear_eventfd来消耗掉此事件。由此主线程达到了唤醒的目的,然后下次循环,通过 am.ExecuteOneCommand 来取出属性事件对应的action来执行。

ActionManager::ExecuteOneCommand

/// @system/core/init/action_manager.cpp
void ActionManager::ExecuteOneCommand() {
    {
        auto lock = std::lock_guard{event_queue_lock_};
        // Loop through the event queue until we have an action to execute
        while (current_executing_actions_.empty() && !event_queue_.empty()) { // 当前没有执行的action,但是有事件
            for (const auto& action : actions_) { // 找到此事件对应要处理的action
                if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
                               event_queue_.front())) {
                    current_executing_actions_.emplace(action.get()); // 一个事件可以对应多个 action
                }
            }
            event_queue_.pop();
        }
    }

    if (current_executing_actions_.empty()) {
        return;
    }
    // 返回 queue 中第一个元素的引用,而不是删除
    auto action = current_executing_actions_.front();

    if (current_command_ == 0) { // 处理此action的第一条命令时打印
        std::string trigger_name = action->BuildTriggersString();
        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
                  << ":" << action->line() << ")";
    }

    action->ExecuteOneCommand(current_command_); // 回调每个action匹配的命令

    // If this was the last command in the current action, then remove
    // the action from the executing list.
    // If this action was oneshot, then also remove it from actions_.
    ++current_command_;  // 每执行一次数量加1
    if (current_command_ == action->NumCommands()) { // 如果此 action的命令全部执行完毕
        current_executing_actions_.pop(); // 从正执行列表删除
        current_command_ = 0;
        if (action->oneshot()) { // 如果是一次性的 还有从action列表移除。 通常Builtin Action是一次性的
            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
                           actions_.end());
        }
    }
}

接下来的流程和 init(2) rc脚本解析和事件执行流程 类似,就不在赘述了。

示例:设置属性 persist.traced.enable

当设置属性 persist.traced.enable 为 1, 以下动作将会触发,会依次执行 mkdir命令和 start 命令。

/// perfetto.rc
on property:persist.traced.enable=1
    # Trace files need to be:
    # - Written by either uid:shell or uid:statsd.
    # - Read by shell and incidentd.
    mkdir /data/misc/perfetto-traces 0773 root shell

    # Traces in this directory are only accessed by dumpstate (read+unlink) and
    # by the bug reporting UI (ls+getattr).
    mkdir /data/misc/perfetto-traces/bugreport 0773 root shell

    # This directory allows shell to save configs file in a place where the
    # perfetto cmdline client can read then. /data/local/tmp/ isn't safe because
    # too many other domains can write into that. See b/170404111.
    mkdir /data/misc/perfetto-configs 0775 root shell

    start traced
    start traced_probes

获取属性流程

接下来分析如何获取属性,以java层的SystemProperties的get获取方式来分析这个过程

SystemProperties#get

frameworks/base/core/java/android/os/SystemProperties.java
/**
 * Get the String value for the given {@code key}.
 *
 * @param key the key to lookup
 * @return an empty string if the {@code key} isn't found
 * @hide
 */
@NonNull
@SystemApi
public static String get(@NonNull String key) {
    if (TRACK_KEY_ACCESS) onKeyAccess(key);
    return native_get(key);
}

// The one-argument version of native_get used to be a regular native function.
@UnsupportedAppUsage
private static String native_get(String key) {
    return native_get(key, "");
}

@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native String native_get(String key, String def); // 最后调用  native 方法

native_get方法 对应的jni函数是SystemProperties_getSS

frameworks/base/core/jni/android_os_SystemProperties.cpp
const JNINativeMethod method_table[] = {
    { "native_get",
      "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
      (void*) SystemProperties_getSS },
      ...
}

SystemProperties_getSS

/// @frameworks/base/core/jni/android_os_SystemProperties.cpp
jstring SystemProperties_getSS(JNIEnv* env, jclass clazz, jstring keyJ, jstring defJ)
{
    jstring ret = defJ;
    ReadProperty(env, keyJ, [&](const char* value) { // 读取属性,此处lambda是Functor回调
        if (value[0]) {
            ret = env->NewStringUTF(value);
        }
    });
    if (ret == nullptr && !env->ExceptionCheck()) {
      ret = env->NewStringUTF("");  // Legacy behavior
    }
    return ret;
}

ReadProperty

template<typename Functor>
void ReadProperty(JNIEnv* env, jstring keyJ, Functor&& functor)
{
    ScopedUtfChars key(env, keyJ);
    if (!key.c_str()) {
        return;
    }
#if defined(__BIONIC__)  // 使用了bionic
    const prop_info* prop = __system_property_find(key.c_str());//获取key对应的prop info
    if (!prop) {
        return;
    }
    ReadProperty(prop, std::forward<Functor>(functor)); // 读取value
#else
    std::forward<Functor>(functor)(
        android::base::GetProperty(key.c_str(), "").c_str());
#endif
}

// 调用另一个 ReadProperty 函数
template<typename Functor>
void ReadProperty(const prop_info* prop, Functor&& functor)
{
#if defined(__BIONIC__)
    auto thunk = [](void* cookie,
                    const char* /*name*/,
                    const char* value,
                    uint32_t /*serial*/) {
        std::forward<Functor>(*static_cast<Functor*>(cookie))(value); // 回调 Functor
    };
    __system_property_read_callback(prop, thunk, &functor); // 读取value并回调
#else
    LOG(FATAL) << "fast property access supported only on device";
#endif
}

__system_property_find

/// @bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
const prop_info* __system_property_find(const char* name) {
  return system_properties.Find(name); // 寻找name对应context的映射区域内的prop信息
}

/// @bionic/libc/system_properties/system_properties.cpp
const prop_info* SystemProperties::Find(const char* name) {
  if (!initialized_) {// 未初始化则返回
    return nullptr;
  }

  prop_area* pa = contexts_->GetPropAreaForName(name); // 找到name对应的映射区
  if (!pa) {
    async_safe_format_log(ANDROID_LOG_WARN, "libc", "Access denied finding property \"%s\"", name);
    return nullptr;
  }

  return pa->find(name); // 从这个区域寻找对应的属性信息
}

ContextsSerialized::GetPropAreaForName

prop_area* ContextsSerialized::GetPropAreaForName(const char* name) {
  uint32_t index;
  property_info_area_file_->GetPropertyInfoIndexes(name, &index, nullptr);// 根据name获取context在集合中的index
  if (index == ~0u || index >= num_context_nodes_) {
    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Could not find context for property \"%s\"",
                          name);
    return nullptr;
  }
  auto* context_node = &context_nodes_[index]; // 获取name对应的 context_node
  if (!context_node->pa()) { // 如果还没映射该节点
    // We explicitly do not check no_access_ in this case because unlike the
    // case of foreach(), we want to generate an selinux audit for each
    // non-permitted property access in this function.
    context_node->Open(false, nullptr); // 需要映射此节点,注意第一个参数为false,只读
  }
  return context_node->pa(); // 返回映射区域
}

如之前,在ContextNode::Open中传入参数为false,会调用prop_area::map_prop_area映射到内存

prop_area* prop_area::map_prop_area(const char* filename) {
  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); //只读方式打开
  if (fd == -1) return nullptr;

  prop_area* map_result = map_fd_ro(fd); // 只读映射到内存
  close(fd);

  return map_result;
}

__system_property_read_callback

此处上面的callback是上面传过来的thunk,cookie 是传的Functor

/// @bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
void __system_property_read_callback(const prop_info* pi,
                                     void (*callback)(void* cookie, const char* name,
                                                      const char* value, uint32_t serial),
                                     void* cookie) {
  return system_properties.ReadCallback(pi, callback, cookie);
}

SystemProperties::ReadCallback

/// @bionic/libc/system_properties/system_properties.cpp
void SystemProperties::ReadCallback(const prop_info* pi,
                                    void (*callback)(void* cookie, const char* name,
                                                     const char* value, uint32_t serial),
                                    void* cookie) {
  // Read only properties don't need to copy the value to a temporary buffer, since it can never
  // change.  We use relaxed memory order on the serial load for the same reason.
  if (is_read_only(pi->name)) { // ro. 只读属性,value不会改变
    uint32_t serial = load_const_atomic(&pi->serial, memory_order_relaxed);
    if (pi->is_long()) {
      callback(cookie, pi->name, pi->long_value(), serial);
    } else {
      callback(cookie, pi->name, pi->value, serial);
    }
    return;
  }

  char value_buf[PROP_VALUE_MAX];
  uint32_t serial = ReadMutablePropertyValue(pi, value_buf); // 读取可变的属性值
  callback(cookie, pi->name, value_buf, serial); // 回调获取得到值
}

SystemProperties::ReadMutablePropertyValue

#define	__predict_true(exp)	__builtin_expect((exp) != 0, 1)
#define	__predict_false(exp)	__builtin_expect((exp) != 0, 0)

uint32_t SystemProperties::ReadMutablePropertyValue(const prop_info* pi, char* value) {
  // We assume the memcpy below gets serialized by the acquire fence.
  uint32_t new_serial = load_const_atomic(&pi->serial, memory_order_acquire);
  uint32_t serial;
  unsigned int len;
  for (;;) {
    serial = new_serial;
    len = SERIAL_VALUE_LEN(serial);
    if (__predict_false(SERIAL_DIRTY(serial))) { // 检测到数据有更新
      // See the comment in the prop_area constructor.
      prop_area* pa = contexts_->GetPropAreaForName(pi->name);
      memcpy(value, pa->dirty_backup_area(), len + 1);
    } else {
      memcpy(value, pi->value, len + 1); // 拷贝value数据
    }
    atomic_thread_fence(memory_order_acquire);
    new_serial = load_const_atomic(&pi->serial, memory_order_relaxed);
    if (__predict_true(serial == new_serial)) { // 没有数据改变,跳出
      break;
    }
    // We need another fence here because we want to ensure that the memcpy in the
    // next iteration of the loop occurs after the load of new_serial above. We could
    // get this guarantee by making the load_const_atomic of new_serial
    // memory_order_acquire instead of memory_order_relaxed, but then we'd pay the
    // penalty of the memory_order_acquire even in the overwhelmingly common case
    // that the serial number didn't change.
    atomic_thread_fence(memory_order_acquire);
  }
  return serial;
}

总结一下ReadProperty获取属性的流程

  • __system_property_find 寻找prop_info
    • 获取key对应的ContextNode的内存映射区域prop_area
    • 如果节点没有,则会打开并以只读映射到内存
    • 从prop_area中读取prop_info
  • __system_property_read_callback 从prop info获取value,并回传value值
    • 从prop_info中读取value
    • callback回调传回value值

在上面SystemProperties::Find中,如果initialized_为false则返回,那么什么时候进行初始化呢?对于Android中的应用或native程序而言,它在启动时会首先加载linker模块做一些初始化,其中就包括属性初始化,主要看linker_main函数

linker_main

/// bionic/linker/linker_main.cpp
static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load) {
  ...
  // Initialize system properties
  // 此处做初始化
  __system_properties_init(); // may use 'environ'

  // Initialize platform properties.
  platform_properties_init();

  // Register the debuggerd signal handler.
  linker_debuggerd_init(); // 初始化 signal handler
  ...
}

__system_properties_init

/// @bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_properties_init() {// PROP_FILENAME 是 /dev/__properties__
  return system_properties.Init(PROP_FILENAME) ? 0 : -1;
}

SystemProperties::Init

/// @bionic/libc/system_properties/system_properties.cpp
bool SystemProperties::Init(const char* filename) {
  // This is called from __libc_init_common, and should leave errno at 0 (http://b/37248982).
  ErrnoRestorer errno_restorer;

  if (initialized_) {
    contexts_->ResetAccess();
    return true;
  }

  if (strlen(filename) >= PROP_FILENAME_MAX) {
    return false;
  }
  strcpy(property_filename_, filename);

  if (is_dir(property_filename_)) { // 判断是目录,已在init中创建
    // property_info 可读,创建 ContextsSerialized 并初始化
    if (access("/dev/__properties__/property_info", R_OK) == 0) {
      contexts_ = new (contexts_data_) ContextsSerialized();
      if (!contexts_->Initialize(false, property_filename_, nullptr)) {
        return false;
      }
    } else { // 否则创建 ContextsSplit 并初始化
      contexts_ = new (contexts_data_) ContextsSplit 并初始化();
      if (!contexts_->Initialize(false, property_filename_, nullptr)) {
        return false;
      }
    }
  } else { // 否则创建 ContextsPreSplit 并初始化
    contexts_ = new (contexts_data_) ContextsPreSplit();
    if (!contexts_->Initialize(false, property_filename_, nullptr)) {
      return false;
    }
  }
  initialized_ = true; // 标记已初始化
  return true;
}

以ContextsSerialized的初始化为例

/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
  filename_ = filename;
  if (!InitializeProperties()) {
    return false;
  }
  // 从上面传递的writable值为false
  if (writable) { // 在init中走这个逻辑
    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 { // 走这个逻辑,将 properties_serial 映射到内存
    if (!MapSerialPropertyArea(false, nullptr)) {
      FreeAndUnmap();
      return false;
    }
  }
  return true;
}

从上面分析看,只是将properties_serial映射到了内存,而并没有映射其他ContextNode对应的文件节点。

设置属性流程

接下来,以 SystemProperties#set 设置属性来分析该流程。

SystemProperties#set

/// @frameworks/base/core/java/android/os/SystemProperties.java
/**
 * Set the value for the given {@code key} to {@code val}.
 *
 * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
 * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
 * SELinux. libc will log the underlying reason.
 * @hide
 */
@UnsupportedAppUsage
public static void set(@NonNull String key, @Nullable String val) {
    if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
        throw new IllegalArgumentException("value of system property '" + key
                + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
    }
    if (TRACK_KEY_ACCESS) onKeyAccess(key);
    native_set(key, val); // 对应native方法
}

// _NOT_ FastNative: native_set performs IPC and can block
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native void native_set(String key, String def); // 跨进程可能阻塞,不能是FastNative

SystemProperties_set

native_set 对应的jni函数是 SystemProperties_set

/// @frameworks/base/core/jni/android_os_SystemProperties.cpp
{ "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
  (void*) SystemProperties_set },

  void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
                            jstring valJ)
  {
      ScopedUtfChars key(env, keyJ); // 封装,将jstring转char*
      if (!key.c_str()) {
          return;
      }
      std::optional<ScopedUtfChars> value;
      if (valJ != nullptr) {
          value.emplace(env, valJ);
          if (!value->c_str()) {
              return;
          }
      }
      bool success;
  #if defined(__BIONIC__)  // android 定义了 __BIONIC__
      success = !__system_property_set(key.c_str(), value ? value->c_str() : "");
  #else
      success = android::base::SetProperty(key.c_str(), value ? value->c_str() : "");
  #endif
      if (!success) {
          jniThrowException(env, "java/lang/RuntimeException",
                            "failed to set system property (check logcat for reason)");
      }
  }

__system_property_set

/// @bionic/libc/bionic/system_property_set.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_set(const char* key, const char* value) {
  if (key == nullptr) return -1;
  if (value == nullptr) value = "";

  if (g_propservice_protocol_version == 0) { // 默认为0,需要获取协议版本
    detect_protocol_version();
  }

  if (g_propservice_protocol_version == kProtocolVersion1) { // 老协议,使用 PROP_MSG_SETPROP
    // Old protocol does not support long names or values
    if (strlen(key) >= PROP_NAME_MAX) return -1;
    if (strlen(value) >= PROP_VALUE_MAX) return -1;

    prop_msg msg;
    memset(&msg, 0, sizeof msg);
    msg.cmd = PROP_MSG_SETPROP;
    strlcpy(msg.name, key, sizeof msg.name);
    strlcpy(msg.value, value, sizeof msg.value);

    return send_prop_msg(&msg);
  } else { // 新协议,使用 PROP_MSG_SETPROP2
    // New protocol only allows long values for ro. properties only.
    if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
    // Use proper protocol
    PropertyServiceConnection connection; // 连接属性服务的socket服务端
    if (!connection.IsValid()) {
      errno = connection.GetLastError();
      async_safe_format_log(
          ANDROID_LOG_WARN, "libc",
          "Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", key, value,
          errno, strerror(errno));
      return -1;
    }

    SocketWriter writer(&connection); // 传给SocketWriter,用它来进行读写
    // 向 init 属性服务 server socket 写入属性键值
    if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
      errno = connection.GetLastError();
      async_safe_format_log(ANDROID_LOG_WARN, "libc",
                            "Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
                            key, value, errno, strerror(errno));
      return -1;
    }

    int result = -1;
    // 读取服务端的回复
    if (!connection.RecvInt32(&result)) {
      errno = connection.GetLastError();
      async_safe_format_log(ANDROID_LOG_WARN, "libc",
                            "Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)",
                            key, value, errno, strerror(errno));
      return -1;
    }

    if (result != PROP_SUCCESS) { // 不成功,打印失败log
      async_safe_format_log(ANDROID_LOG_WARN, "libc",
                            "Unable to set property \"%s\" to \"%s\": error code: 0x%x", key, value,
                            result);
      return -1;
    }

    return 0;
  }
}

detect_protocol_version

#define PROP_SERVICE_NAME "property_service"
#define PROP_FILENAME "/dev/__properties__"

/// @bionic/libc/bionic/system_property_set.cpp
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static const char* kServiceVersionPropertyName = "ro.property_service.version";

static constexpr uint32_t kProtocolVersion1 = 1;
static constexpr uint32_t kProtocolVersion2 = 2;  // current
static atomic_uint_least32_t g_propservice_protocol_version = 0;

static void detect_protocol_version() {
  char value[PROP_VALUE_MAX];
  // 获取属性服务版本
  if (__system_property_get(kServiceVersionPropertyName, value) == 0) {
    g_propservice_protocol_version = kProtocolVersion1; // 为0,默认使用协议1
    async_safe_format_log(ANDROID_LOG_WARN, "libc",
                          "Using old property service protocol (\"%s\" is not set)",
                          kServiceVersionPropertyName);
  } else { // 否则使用获取的版本
    uint32_t version = static_cast<uint32_t>(atoll(value));
    if (version >= kProtocolVersion2) { // 协议2
      g_propservice_protocol_version = kProtocolVersion2;
    } else {  // 协议1
      async_safe_format_log(ANDROID_LOG_WARN, "libc",
                            "Using old property service protocol (\"%s\"=\"%s\")",
                            kServiceVersionPropertyName, value);
      g_propservice_protocol_version = kProtocolVersion1;
    }
  }
}

从机器获取的属性值是2,也就是当前使用的是协议2

$ getprop ro.property_service.version                                                                                          
2

建立socket连接

在PropertyServiceConnection的构造函数中,建立与属性服务端socket的连接。

PropertyServiceConnection() : last_error_(0) {
  socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));// 创建client socket
  if (socket_.get() == -1) {
    last_error_ = errno;
    return;
  }

  const size_t namelen = strlen(property_service_socket);
  sockaddr_un addr;
  memset(&addr, 0, sizeof(addr));
  strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
  addr.sun_family = AF_LOCAL;
  socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
  // 连接 server socket
  if (TEMP_FAILURE_RETRY(connect(socket_.get(),
                                 reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
    last_error_ = errno;
    socket_.reset();
  }
}

写属性

class SocketWriter {
 public:
  explicit SocketWriter(PropertyServiceConnection* connection)
      : connection_(connection), iov_index_(0), uint_buf_index_(0) {
  }
  // 写 uint32_t 数据
  SocketWriter& WriteUint32(uint32_t value) {
    CHECK(uint_buf_index_ < kUintBufSize);
    CHECK(iov_index_ < kIovSize);
    uint32_t* ptr = uint_buf_ + uint_buf_index_;
    uint_buf_[uint_buf_index_++] = value;
    iov_[iov_index_].iov_base = ptr;
    iov_[iov_index_].iov_len = sizeof(*ptr);
    ++iov_index_;
    return *this;
  }
  // 写 字符串 数据
  SocketWriter& WriteString(const char* value) {
    uint32_t valuelen = strlen(value);
    WriteUint32(valuelen);
    if (valuelen == 0) {
      return *this;
    }

    CHECK(iov_index_ < kIovSize);
    iov_[iov_index_].iov_base = const_cast<char*>(value);
    iov_[iov_index_].iov_len = valuelen;
    ++iov_index_;

    return *this;
  }
  // 发送数据给server端
  bool Send() {
    if (!connection_->IsValid()) {
      return false;
    }

    if (writev(connection_->socket(), iov_, iov_index_) == -1) {
      connection_->last_error_ = errno;
      return false;
    }

    iov_index_ = uint_buf_index_ = 0;
    return true;
  }

 private:
  ...
  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(SocketWriter);
};

读回复

class PropertyServiceConnection {
public:
  ...
  // 读取 int32_t 数据
  bool RecvInt32(int32_t* value) {
    int result = TEMP_FAILURE_RETRY(recv(socket_.get(), value, sizeof(*value), MSG_WAITALL));
    return CheckSendRecvResult(result, sizeof(*value));
  }

private:
  bool CheckSendRecvResult(int result, int expected_len) { // 检查结果是否错误
    if (result == -1) {
      last_error_ = errno;
    } else if (result != expected_len) {
      last_error_ = -1;
    } else {
      last_error_ = 0;
    }

    return last_error_ == 0;
  }
}

当init属性服务收到客户端收到的设置属性请求后,会回调handle_property_set_fd函数来处理请求,可以参照之前的分析。

总结

  • init初始化属性内存区域,根据每个property context创建对应的ContextNode,建立节点文件并以读写映射到内存
  • init从各个build.prop文件中加载属性值
  • init启动属性服务,创建服务端socket,等待客户端连接来设置属性
  • 客户端通过socket连接属性服务端socket,通过socket写数据来设置属性值
  • 客户端通过打开节点文件并以只读映射到内存,然后读取对应的属性值

你可能感兴趣的:(Android,Android源码解析,init,android,init)