首页 > 其他分享 >Android(S)系统属性服务详解

Android(S)系统属性服务详解

时间:2023-10-19 16:24:47浏览次数:43  
标签:__ return system value 详解 Android property properties 属性

1.简介

  Android系统中,为统一管理系统的属性,设计了一个统一的属性系统,包括两部分:文件保存的持久属性和每次开机导入的cache属性,前者主要保存在.prop文件中,需要注意的是android属性的名称是有一定的格式要求。每个属性都有一个名称和值,他们都是字符串格式。属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。 系统属性根据不同的应用类型,分为不可变型,持久型,网络型,启动和停止服务等。

  •   属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。
  •   属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property/persistent_properties。(相对旧一点的Android版本是/data/property)
  •   属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)
  •   属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。

2.API使用

Java API:

import android.os.SystemProperties;
public static String get(String key, String def) {}
public static void set(String key, String val) {}

//示例
//Systemproperties类在android.os下,但这个类是隐藏的,上层程序开发无法直接使用
//需要系统属性设置的程序也必须有system或root权限,或者用Java的反射机制
需要注意的是android属性的名称是有一定的格式要求的,前缀必须用system\core\init\property_service.c中定义的前缀
Systemproperties.set(name, value);//设置
Systemproperties.get(name);//读取

Native API:

#include <cutils/properties.h>
int property_get(const char *key, char *value, const char *default_value); 
int property_set(const char *key, const char *value);

3.系统属性服务启动流程

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

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

属性系统服务端源码:system/core/init/property_service.cpp

(1)初始化(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();
}

从相关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

将 /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;
}
/// @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;
}
/// @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;
}
/// @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;
}
/// @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函数:

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

最后将 /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()流程:

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

以上 __system_property_area_init() 完成,创建了所有context对应的节点并映射到内存以及映射properties_serial。

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

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

服务启动时会加载内核参数初始化相关属性值:

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

开机加载默认属性:

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

    // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
    // Falling back to /<part>/defalt.prop and /<part>/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.<partition>.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配置属性
}

更新usb相关的配置属性:

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

(2)启动服务(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);
}
/// @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,监听其相关事件,当收到设置属性请求时,会回调 handle_property_set_fd 函数
    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)();
            }
        }
    }
}
/// @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;
    }
}
/// @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); // 真正设置更新
}
/// @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;
}

WritePersistentProperty实现是在 system/core/init/persistent_properties.cpp:

/// @system/core/init/persistent_properties.cpp
// Persistent properties are not written often, so we rather not keep any data in memory and read
// then rewrite the persistent property file for each update.
void WritePersistentProperty(const std::string& name, const std::string& value) {
    auto persistent_properties = LoadPersistentPropertyFile();

    if (!persistent_properties.ok()) {
        LOG(ERROR) << "Recovering persistent properties from memory: "
                   << persistent_properties.error();
        persistent_properties = LoadPersistentPropertiesFromMemory();
    }
    auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
                           persistent_properties->mutable_properties()->end(),
                           [&name](const auto& record) { return record.name() == name; });
    if (it != persistent_properties->mutable_properties()->end()) {
        it->set_name(name);
        it->set_value(value);
    } else {
        AddPersistentProperty(name, value, &persistent_properties.value());
    }

    if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) { //persistent属性要接入文件永久存储
        LOG(ERROR) << "Could not store persistent property: " << result.error();
    }
}
/// @system/core/init/persistent_properties.cpp
Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
    const std::string temp_filename = persistent_property_filename + ".tmp";
    unique_fd fd(TEMP_FAILURE_RETRY(
        open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
    if (fd == -1) {
        return ErrnoError() << "Could not open temporary properties file";
    }
    std::string serialized_string;
    if (!persistent_properties.SerializeToString(&serialized_string)) {
        return Error() << "Unable to serialize properties";
    }
    if (!WriteStringToFd(serialized_string, fd)) {  //属性写入临时文件
        return ErrnoError() << "Unable to write file contents";
    }
    fsync(fd);
    fd.reset();

    if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) { //临时文件重命名为/data/property/persistent_properties
        int saved_errno = errno;
        unlink(temp_filename.c_str());
        return Error(saved_errno) << "Unable to rename persistent property file";
    }

//注:以上rename库函数并不会立即将文件写入磁盘,它只是将文件在文件系统中的目录项(包括文件名和对应的索引节点)进行修改,以实现文件的重命名操作。
//而Android 10及之前版本没有以下目录sync的动作,所以有些不带电池的设备,在设置persist属性后如果突然断电,重启后设置的属性值可能会丢失!
// rename() is atomic with regards to the kernel's filesystem buffers, but the parent // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage. // Note in this case, that the source and destination directories are the same, so only one // fsync() is required. auto dir = Dirname(persistent_property_filename); auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)}; if (dir_fd < 0) { return ErrnoError() << "Unable to open persistent properties directory for fsync()"; } fsync(dir_fd); return {}; }

属性变化的处理逻辑:

/// @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); // 检查是否有设置等待此属性值
}
/// @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)); // 插入一个属性变化的事件
}
/// @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来执行。

/// @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());
        }
    }
}

4.属性的读写

  前面提到了persist属性的设置,接下来具体分析下属性的读取和设置流程。

(1)读取属性,以java层的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 方法
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 },
      ...
}
/// @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;
}
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
}
/// @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); // 从这个区域寻找对应的属性信息
}
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;
}

此处上面的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);
}
/// @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); // 回调获取得到值
}
#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获取属性的流程

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

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

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

(2)设置属性,以 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
/// @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)");
      }
  }
/// @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;
  }
}
#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;
    }
  }
}

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

通过socket写属性:

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写数据来设置属性值。
  • 客户端通过打开节点文件并以只读映射到内存,然后读取对应的属性值。

 

标签:__,return,system,value,详解,Android,property,properties,属性
From: https://www.cnblogs.com/blogs-of-lxl/p/17774765.html

相关文章

  • 如何在Android中获取图片路径
    Android中获取图片路径的方法在Android开发中,获取图片路径是一个常见的需求。下面介绍几种途径来实现获取图片路径的方法。途径一:从相册中获取图片路径要从相册中获取图片的路径,我们需要调用相应的系统接口实现。具体步骤如下:在Manifest文件中添加获取相册的权限:<uses-per......
  • 神经网络基础篇:详解二分类(Binary Classification)
    二分类注:当实现一个神经网络的时候,通常不直接使用for循环来遍历整个训练集(编程tips)举例逻辑回归逻辑回归是一个用于二分类(binaryclassification)的算法。首先从一个问题开始说起,这里有一个二分类问题的例子,假如有一张图片作为输入,比如这只猫,如果识别这张图片为猫,则输出标签......
  • huawei交换机基本配置,入门视图详解
    一、交换机基本配置1、交换机连接方式本地:计算机COM口/USB口-->Console线-->交换机Console口远程:Putty、SecureCRT、Xshell远程管理工具2、网络操作系统1)有四种视图模式<*> //用户视图:查看运行状态或其他参数[*] //系统视图:配置设备的系统参数[*-GigabitEthernet0/0......
  • Unity3D 如何制作带厚度的透明图片详解
    Unity3D是一款功能强大的游戏开发引擎,可以实现各种复杂的游戏效果。本文将详细介绍如何使用Unity3D制作带厚度的透明图片,并提供代码实现。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的技术大佬,欢迎你来交流学习。在Unity3D中,......
  • 3D游戏开发中的数学知识矩阵详解
    矩阵很多同学没有接触过,所以感觉很难,很复杂,其实只要学过矩阵的同学都知道,矩阵运算并不难。今天我们给大家讲讲游戏开发中的矩阵的运算。1:矩阵是什么?矩阵是描述线性变换的一种数学工具,线性变换指的是使用一次函数从一个空间变换到另外一个空间。例如在空间A中的一个2维向量(xa......
  • LSTM-CRF模型详解和Pytorch代码实现
    在快速发展的自然语言处理领域,Transformers已经成为主导模型,在广泛的序列建模任务中表现出卓越的性能,包括词性标记、命名实体识别和分块。在Transformers之前,条件随机场(CRFs)是序列建模的首选工具,特别是线性链CRFs,它将序列建模为有向图,而CRFs更普遍地可以用于任意图。本文中crf......
  • 最新Unity 如何打包发布到Android
    Unity打包Android现在UnityHub已经做的非常好了,可以直接下载对应Unity版本所对应得JDK,NDK,SDK,直接可以通过unityhub下载避免了,各种版本不同导致的编译问题,大大的降低了初学者的打包发布的难度,本文详细讲解UnityAndroid打包以及在打包中间需要理解的点, 如下:(1) Unityhu......
  • javascript之分片上传,断点续传的实际项目实现详解
    首先,我们需要了解什么是分片上传和断点续传。分片上传是将大文件分成多个小块进行上传,每个小块可以独立上传,从而提高上传速度和稳定性。而断点续传是指在上传大文件时,当上传过程中因断网或其他原因中断,再次上传时可以不用重头开始,而是从中断的地方继续上传。接下来是分片上传和......
  • Python入门进阶:68 个 Python 内置函数详解
    内置函数就是Python给你提供的,拿来直接用的函数,比如print.,input等。截止到python版本3.6.2,python一共提供了68个内置函数,具体如下abs()dict()help()min()setattr()all()dir()hex()next()slice()any()divmod()id()object()sorted()ascii()enumerate()input()......
  • Windows下VC++编译器32位memcpy、memmove函数汇编代码详解
    整理者:赤勇玄心行天道QQ号:280604597微信号:qq280604597QQ群:511046632博客:www.cnblogs.com/gaoyaguo  blog.csdn.net/cyz7758520?type=blog大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的!你可以随意转载,无需注明出处!写文档实属不易,我希望大家能支......