Property Service源码分析

属性服务初始化

这个主要是初始化系统属性区域,加载内核和设备树的属性配置,并设置默认的启动属性。它是 Android 启动过程中非常关键的一部分,用于确保系统属性在启动时被正确加载和管理。这些属性会影响系统配置、启动行为、安全策略等各个方面:

加载并初始化了以下属性相关内容:

  • /dev/properties/property_info 的初始化并映射至内存
  • 加载内核设备树的属性信息
  • 加载内核命令行的属性信息
  • 加载启动配置的属性信息
  • 加载系统启动的默认属性信息
  1. 属性服务初始化入口:PropetyInit()


    PropetyInit() 代码:
c 复制代码
void PropertyInit() {
    // 设置selinux 日志回调
    selinux_callback cb;

    // 回调内容就是一些打印日志
    cb.func_audit = PropertyAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    // 创建属性目录
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);

    // 创建序列化的属性信息
    CreateSerializedPropertyInfo();

    // 属性共享内存初始化, 位于bionic/libc/system_properties
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }

    // 加载序列化属性信息的默认路径文件,并运行mmap 映射到内存,直接操作内存,效率更高
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.

    // 处理来自设备树的参数
    ProcessKernelDt();

    // 处理来自命令行的属性
    ProcessKernelCmdline();

    // 处理来自boot 的属性
    ProcessBootconfig();

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

    PropertyLoadBootDefaults();
}
  1. selinux 回调
c 复制代码
// 设置selinux 日志回调
selinux_callback cb;

// 回调内容就是一些打印日志
cb.func_audit = PropertyAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);

static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
    auto* d = reinterpret_cast<PropertyAuditData*>(data);

    if (!d || !d->name || !d->cr) {
        LOG(ERROR) << "AuditCallback invoked with null data arguments!";
        return 0;
    }

    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
             d->cr->gid);
    return 0;
}
  1. 创建 /dev/__propertities__ 目录
c 复制代码
// 创建属性目录
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
  1. 创建序列化的属性信息 CreateSerializedPropertyInfo
c 复制代码
bool LoadPropertyInfoFromFile(const std::string& filename,
                              std::vector<PropertyInfoEntry>* property_infos) {
    auto file_contents = std::string();

    // 读取属性文件
    if (!ReadFileToString(filename, &file_contents)) {
        PLOG(ERROR) << "Could not read properties from '" << filename << "'";
        return false;
    }

    auto errors = std::vector<std::string>{};
    bool require_prefix_or_exact = SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__;

    // 解析属性文件
    ParsePropertyInfoFile(file_contents, require_prefix_or_exact, property_infos, &errors);
    // Individual parsing errors are reported but do not cause a failed boot, which is what
    // returning false would do here.
    for (const auto& error : errors) {
        LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
    }

    return true;
}

// 获取不同分区及不同目录下的属性上下文文件并加载到 property_infos
void CreateSerializedPropertyInfo() {
    auto property_infos = std::vector<PropertyInfoEntry>();
    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
                                      &property_infos)) {
            return;
        }
        // Don't check for failure here, 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("/dev/selinux/apex_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
        }
        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 (access("/vendor/etc/selinux/vendor_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_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);
        LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/dev/selinux/apex_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;
    }

    // 将序列化的数据写入property_info 文件
    constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
    if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
        PLOG(ERROR) << "Unable to write serialized property infos to file";
    }

    // 恢复这个属性文件的上下文
    selinux_android_restorecon(kPropertyInfosPath, 0);
}
  1. 属性共享内存初始化 __system_property_area_init
c 复制代码
__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
if (__system_property_area_init()) {
    LOG(FATAL) << "Failed to initialize property area";
}
  1. 加载序列化属性信息的默认路径文件 property_info_area.LoadDefaultPath()
c 复制代码
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) {
    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;
}

// 加载序列化属性信息的默认路径文件,并运行mmap 映射到内存,直接操作内存,效率更高
if (!property_info_area.LoadDefaultPath()) {
	LOG(FATAL) << "Failed to load serialized property info file";
}
  1. 处理来自内核设备树的参数 ProcessKernelDt
c 复制代码
static void ProcessKernelDt() {
    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
        return;
    }

    // 打开dt 目录
    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
    if (!dir) return;

    std::string dt_file;
    struct dirent* dp;

    // 遍历读取目录内容
    while ((dp = readdir(dir.get())) != NULL) {
        // 过滤操作
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") ||
            !strcmp(dp->d_name, "name")) {
            continue;
        }

        // 获取文件名
        std::string file_name = get_android_dt_dir() + dp->d_name;

        // 读取文件内容:就是属性值存放到dt_file
        android::base::ReadFileToString(file_name, &dt_file);

        // 替换符号
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        // 设置属性ro.boot.xxx
        InitPropertySet("ro.boot."s + dp->d_name, dt_file);
    }
}
  1. 处理来自命令行的属性 ProcessKernelCmdline()
c 复制代码
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
    std::string error;
    auto result = HandlePropertySetNoSocket(name, value, kInitContext, cr, &error);
    if (result != PROP_SUCCESS) {
        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
    }

    return result;
}


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

constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;

static void ProcessKernelCmdline() {
    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
        if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
            InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
        }
    });
}
  1. 处理来自boot 的属性 ProcessBootconfig()
c 复制代码
static void ProcessBootconfig() {
    ImportBootconfig([&](const std::string& key, const std::string& value) {
        if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
            InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
        }
    });
}
  1. 导出内核变量 ExportKernelBootProps()
c 复制代码
static void ExportKernelBootProps() {
    constexpr const char* UNSET = "";
    struct {
        const char* src_prop;
        const char* dst_prop;
        const char* default_value;
    } prop_map[] = {
            // clang-format off
        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
            // clang-format on
    };
    for (const auto& prop : prop_map) {
        std::string value = GetProperty(prop.src_prop, prop.default_value);
        if (value != UNSET) InitPropertySet(prop.dst_prop, value);
    }
}
  1. 加载默认启动属性 PropertyLoadBootDefaults()
c 复制代码
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()) {
        if (auto res = load_properties_from_file("/prop.default", nullptr, &properties);
            !res.ok()) {
            LOG(ERROR) << res.error();
        }
    }

    // /<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) {
        auto path = "/" + partition + "/etc/build.prop";
        if (load_properties_from_file(path.c_str(), nullptr, &properties).ok()) {
            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);

    // system should have build.prop, unlike the other partitions
    if (auto res = load_properties_from_file("/system/build.prop", nullptr, &properties);
        !res.ok()) {
        LOG(WARNING) << res.error();
    }

    load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
    load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
    // 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;
        if (auto res = load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
            !res.ok()) {
            LOG(WARNING) << res.error();
        }
    }

    for (const auto& [name, value] : properties) {
        std::string error;
        if (PropertySetNoSocket(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();
    property_initialize_ro_vendor_api_level();

    update_sys_usb_config();
}

总结

相关推荐
Kapaseker13 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴14 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95272 天前
Andorid Google 登录接入文档
android
黄林晴2 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android