安卓property service系统分析

本文基于安卓11,propertyService在init进程中启动。


init进程相关代码位于/system/core/init/目录下,其中包括property_service.cpp。

android::properties相关代码位于/system/core/property_service/目录。

底层相关代码/bionic/libc/bionic/, /bionic/libc/system_properties/。

init

main.cpp

int main(int argc, char** argv) {
    
    
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif

    if (!strcmp(basename(argv[0]), "ueventd")) {
    
    
        return ueventd_main(argc, argv);
    }
    
    if (argc > 1) {
    
    
        if (!strcmp(argv[1], "subcontext")) {
    
    
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
    
    
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
    
    
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}

The early init boot sequence is broken up into three stages: first stage init, SELinux setup, and second stage init.

init程序main方法中通过命令行参数判断程序启动时执行的动作,分别执行FirstStageMain(argc, argv),SubcontextMain(argc, argv, &function_map),SetupSelinux(argv),SecondStageMain(argc, argv)方法,在SecondStageMain()方法中执行propertyInit()方法初始化系统属性,启动属性服务。

PropertyInit

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);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
    
    
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
    
    
        LOG(FATAL) << "Failed to load serialized property info file";
    }

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

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

    PropertyLoadBootDefaults();
}

一开始是和selinux相关的,创建"/dev/_properties_"文件夹,这个路径很关键,和属性系统相关的文件都在这个目录下。

  1. CreateSerializedPropertyInfo()

读取selinux模块中property相关文件:plat_property_contexts等,将其解析加载到变量property_infos中。property_infos变量为std::vector类型。

void CreateSerializedPropertyInfo() {
    
    
    auto property_infos = std::vector<PropertyInfoEntry>();
    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
    
    
        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
                                      &property_infos)) {
    
    
            return;
        }
        // Don't check for failure here, so we always have a sane list of properties.
        // E.g. In case of recovery, the vendor partition will not have mounted and we
        // still need the system / platform properties to function.
        if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
    
    
            LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                     &property_infos);
        }
        if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
                                      &property_infos)) {
    
    
            // Fallback to nonplat_* if vendor_* doesn't exist.
            LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                     &property_infos);
        }
        if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
    
    
            LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
                                     &property_infos);
        }
        if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
    
    
            LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
        }
    } 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)) {
    
    
        PLOG(ERROR) << "Unable to write serialized property infos to file";
    }
    selinux_android_restorecon(kPropertyInfosPath, 0);
}

BuildTrie(property_infos, “u:object_r:default_prop:s0”, “string”, &serialized_contexts, &error)),通过LOG(ERROR)信息应该是说一个将解析得到的property_infos变量进行serialize property contexts的动作,serialize的结果保存在变量serialized_contexts。然后将序列化的结果写入到"/dev/properties/property_info"文件中。selinux_android_restorecon(kPropertyInfosPath, 0)让文件的上下文恢复成file_contexts文件里初始配置。

  1. __system_property_area_init()

./bionic/libc/bionic/system_property_api.cpp

#define PROP_FILENAME "/dev/__properties__"

int __system_property_area_init() {
    
    
  bool fsetxattr_failed = false;
  return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}

./bionic/libc/system_properties/system_properties.cpp

bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
    
    
  if (strlen(filename) >= PROP_FILENAME_MAX) {
    
    
    return false;
  }
  strcpy(property_filename_, filename);

  contexts_ = new (contexts_data_) ContextsSerialized();
  if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
    
    
    return false;
  }
  initialized_ = true;
  return true;
}

./bionic/libc/system_properties/contexts_serialized.cpp

bool ContextsSerialized::InitializeProperties() {
    
    
  if (!property_info_area_file_.LoadDefaultPath()) {
    
    
    return false;
  }

  if (!InitializeContextNodes()) {
    
    
    FreeAndUnmap();
    return false;
  }

  return true;
}

bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
    
    
  filename_ = filename;
  if (!InitializeProperties()) {
    
    
    return false;
  }

  if (writable) {
    
    
    mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
    bool open_failed = false;
    if (fsetxattr_failed) {
    
    
      *fsetxattr_failed = false;
    }

    for (size_t i = 0; i < num_context_nodes_; ++i) {
    
    
      if (!context_nodes_[i].Open(true, fsetxattr_failed)) {
    
    
        open_failed = true;
      }
    }
    if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed)) {
    
    
      FreeAndUnmap();
      return false;
    }
  } else {
    
    
    if (!MapSerialPropertyArea(false, nullptr)) {
    
    
      FreeAndUnmap();
      return false;
    }
  }
  return true;
}

./system/core/property_service/libpropertyinfoparser/property_info_parser.cpp

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

_system_property_area_init()经过一系列的方法调用,通过mmap()函数将文件"/dev/__properties_/property_info"加载到进程中共享内存。

接下来的判断语句又执行了一遍property_info_area.LoadDefaultPath(),在ContextsSerialized::InitializeProperties()方法中已经做过这个操作了,不是很懂为什么要执行两遍。

  1. ProcessKernelDt()
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");

static void ProcessKernelDt() {
    
    
    if (!is_android_dt_value_expected("compatible", "android,firmware")) {
    
    
        return;
    }

    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
    if (!dir) return;

    std::string dt_file;
    struct dirent* dp;
    while ((dp = readdir(dir.get())) != NULL) {
    
    
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") ||
            !strcmp(dp->d_name, "name")) {
    
    
            continue;
        }

        std::string file_name = get_android_dt_dir() + dp->d_name;

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        InitPropertySet("ro.boot."s + dp->d_name, dt_file);
    }
}

读取设备树"/proc/device-tree/firmware/android/"中的"name"和"compatible"节点内容,添加"ro.boot."前缀后加入到property属性系统中。

  1. ProcessKernelCmdline()
static void ProcessKernelCmdline() {
    
    
    bool for_emulator = false;
    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
    
    
        if (key == "qemu") {
    
    
            for_emulator = true;
        } else if (StartsWith(key, "androidboot.")) {
    
    
            InitPropertySet("ro.boot." + key.substr(12), value);
        }
    });

    if (for_emulator) {
    
    
        ImportKernelCmdline([&](const std::string& key, const std::string& value) {
    
    
            // In the emulator, export any kernel option with the "ro.kernel." prefix.
            InitPropertySet("ro.kernel." + key, value);
        });
    }
}

读取"/pro/cmdline",将"androidboot."开头的变量,添加"ro.boot."前缀后加入到property属性系统中。

  1. ExportKernelBootProps()
static void ExportKernelBootProps() {
    
    
    constexpr const char* UNSET = "";
    struct {
    
    
        const char* src_prop;
        const char* dst_prop;
        const char* default_value;
    } prop_map[] = {
    
    
            // clang-format off
        {
    
     "ro.boot.serialno",   "ro.serialno",   UNSET, },
        {
    
     "ro.boot.mode",       "ro.bootmode",   "unknown", },
        {
    
     "ro.boot.baseband",   "ro.baseband",   "unknown", },
        {
    
     "ro.boot.bootloader", "ro.bootloader", "unknown", },
        {
    
     "ro.boot.hardware",   "ro.hardware",   "unknown", },
        {
    
     "ro.boot.revision",   "ro.revision",   "0", },
            // clang-format on
    };
    for (const auto& prop : prop_map) {
    
    
        std::string value = GetProperty(prop.src_prop, prop.default_value);
        if (value != UNSET) InitPropertySet(prop.dst_prop, value);
    }
}

关注于变量prop_map中列举的几个重要属性,如果之前没有赋值,将其赋值为"unknown"或"0"。

  1. PropertyLoadBootDefaults()
void PropertyLoadBootDefaults() {
    
    
    // TODO(b/117892318): merge prop.default and build.prop files into one
    // 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 (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
    
    
        // Try recovery path
        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
    
    
            // Try legacy path
            load_properties_from_file("/default.prop", nullptr, &properties);
        }
    }
    load_properties_from_file("/system/build.prop", nullptr, &properties);
    load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
    
    
        load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
    } else {
    
    
        load_properties_from_file("/odm/default.prop", nullptr, &properties);
        load_properties_from_file("/odm/build.prop", nullptr, &properties);
    }
    load_properties_from_file("/product/build.prop", nullptr, &properties);
    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);

    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_derive_build_props();

    update_sys_usb_config();
}

PropertyLoadBootDefaults()方法在"/system/etc/prop.default","/system/build.prop"文件中加载相关属性到properties变量中,再通过PropertySet()方法添加到属性系统中(下面会重点分析PropertySet()方法)。

property_initialize_ro_product_props()方法初始化"ro.product."为前缀的属性。

property_derive_build_props()初始化一些编译相关的属性。

static void property_derive_build_props() {
    
    
    property_derive_build_fingerprint();
    property_derive_build_product();
    property_derive_build_description();
    property_derive_build_display_id();
}

update_sys_usb_config()方法设置"persist.sys.usb.config"属性。

PropertySet()

从./system/core/init/property_service.cpp的PropertySet()方法中可以看到,添加属性时是调用的__system_property_add()方法,

./bionic/libc/bionic/system_property_api.cpp

int __system_property_add(const char* name, unsigned int namelen, const char* value,
                          unsigned int valuelen) {
    
    
  return system_properties.Add(name, namelen, value, valuelen);
}

./bionic/libc/system_properties/system_properties.cpp

int SystemProperties::Add(const char* name, unsigned int namelen, const char* value,
                          unsigned int valuelen) {
    
    
  if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
    
    
    return -1;
  }

  if (namelen < 1) {
    
    
    return -1;
  }

  if (!initialized_) {
    
    
    return -1;
  }

  prop_area* serial_pa = contexts_->GetSerialPropArea();
  if (serial_pa == nullptr) {
    
    
    return -1;
  }

  prop_area* pa = contexts_->GetPropAreaForName(name);
  if (!pa) {
    
    
    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
    return -1;
  }

  bool ret = pa->add(name, namelen, value, valuelen);
  if (!ret) {
    
    
    return -1;
  }

  // There is only a single mutator, but we want to make sure that
  // updates are visible to a reader waiting for the update.
  atomic_store_explicit(serial_pa->serial(),
                        atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
                        memory_order_release);
  __futex_wake(serial_pa->serial(), INT32_MAX);
  return 0;
}

initialized_在SystemProperties::Init()方法中被赋值为true,SystemProperties::Add()三个关键方法调用:

  1. contexts_->GetSerialPropArea();_

  2. contexts->GetPropAreaForName(name);

  3. pa->add(name, namelen, value, valuelen);

  4. contexts_->GetSerialPropArea();_

./bionic/libc/system_properties/include/system_properties/contexts_serialized.h

  virtual prop_area* GetSerialPropArea() override {
    
    
    return serial_prop_area_;
  }

变量serial_prop_area_在 ContextsSerialized::Initialize()方法中通过ContextsSerialized::MapSerialPropertyArea()方法初始化。

./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_ =
        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_;
}

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

map_prop_area_rw()通过open()获取文件"/dev/__properties__/properties_serial"的引用,mmap()共享内存,最终返回prop_area对象赋值给变量serial_prop_area_。

  1. contexts->GetPropAreaForName(name);

./bionic/libc/system_properties/contexts_serialized.cpp

prop_area* ContextsSerialized::GetPropAreaForName(const char* name) {
    
    
  uint32_t index;
  property_info_area_file_->GetPropertyInfoIndexes(name, &index, nullptr);
  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];
  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);
  }
  return context_node->pa();
}

通过name找到prop_area。

  1. pa->add(name, namelen, value, valuelen);

./bionic/libc/system_properties/prop_area.cpp

bool prop_area::add(const char* name, unsigned int namelen, const char* value,
                    unsigned int valuelen) {
    
    
  return find_property(root_node(), name, namelen, value, valuelen, true);
}

./bionic/libc/system_properties/prop_area.cpp

const prop_info* prop_area::find_property(prop_bt* const trie, const char* name, uint32_t namelen,
                                          const char* value, uint32_t valuelen,
                                          bool alloc_if_needed) {
    
    
  if (!trie) return nullptr;

  const char* remaining_name = name;
  prop_bt* current = trie;
  while (true) {
    
    
    const char* sep = strchr(remaining_name, '.');
    const bool want_subtree = (sep != nullptr);
    const uint32_t substr_size = (want_subtree) ? sep - remaining_name : strlen(remaining_name);

    if (!substr_size) {
    
    
      return nullptr;
    }

    prop_bt* root = nullptr;
    uint_least32_t children_offset = atomic_load_explicit(&current->children, memory_order_relaxed);
    if (children_offset != 0) {
    
    
      root = to_prop_bt(&current->children);
    } else if (alloc_if_needed) {
    
    
      uint_least32_t new_offset;
      root = new_prop_bt(remaining_name, substr_size, &new_offset);
      if (root) {
    
    
        atomic_store_explicit(&current->children, new_offset, memory_order_release);
      }
    }

    if (!root) {
    
    
      return nullptr;
    }

    current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
    if (!current) {
    
    
      return nullptr;
    }

    if (!want_subtree) break;

    remaining_name = sep + 1;
  }

  uint_least32_t prop_offset = atomic_load_explicit(&current->prop, memory_order_relaxed);
  if (prop_offset != 0) {
    
    
    return to_prop_info(&current->prop);
  } else if (alloc_if_needed) {
    
    
    uint_least32_t new_offset;
    prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
    if (new_info) {
    
    
      atomic_store_explicit(&current->prop, new_offset, memory_order_release);
    }

    return new_info;
  } else {
    
    
    return nullptr;
  }
}

至此property应该添加到属性系统中了。

StartPropertyService(&property_fd)

void StartPropertyService(int* epoll_socket) {
    
    
    InitPropertySet("ro.property_service.version", "2");

    int sockets[2];
    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_socket = sockets[1];
    StartSendingMessages();

    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, {
    
    });
        result.ok()) {
    
    
        property_set_fd = *result;
    } else {
    
    
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    listen(property_set_fd, 8);

    auto new_thread = std::thread{
    
    PropertyServiceThread};
    property_service_thread.swap(new_thread);
}

std::thread{PropertyServiceThread};

static void PropertyServiceThread() {
    
    
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
    
    
        LOG(FATAL) << result.error();
    }

    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
        !result.ok()) {
    
    
        LOG(FATAL) << result.error();
    }

    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()

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

总体就是监听socket设备"/dev/socket/property_service",通过handle_property_set_fd()方法处理socket接收的消息。

Notice

使用属性时应该要注意的地方。

  • persist属性:

以"persist."为前缀的属性,设备重启后依然能够查看到,其保存在"dev/socket/property_service"节点中。

  • ro属性:

以"ro."为前缀的属性,只读属性,不能修改。read only。

  • ctl属性:

"ctl.start"启动服务,"ctl.stop"关闭服务。

  • powerctl属性

"sys.powerctl"属性可控制设备重启。

  • 权限问题

HandlePropertySet()设置属性时会调用CheckPermissions()方法检查是否有相关权限,

uint32_t CheckPermissions(const std::string& name, const std::string& value,
                          const std::string& source_context, const ucred& cr, std::string* error) {
    
    
    if (!IsLegalPropertyName(name)) {
    
    
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }

    if (StartsWith(name, "ctl.")) {
    
    
        if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
    
    
            *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
                                  value.c_str());
            return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
        }

        return PROP_SUCCESS;
    }

    const char* target_context = nullptr;
    const char* type = nullptr;
    property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);

    if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
    
    
        *error = "SELinux permission check failed";
        return PROP_ERROR_PERMISSION_DENIED;
    }

    if (!CheckType(type, value)) {
    
    
        *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
                              (type ?: "(null)"));
        return PROP_ERROR_INVALID_VALUE;
    }

    return PROP_SUCCESS;
}

CheckMacPerms()方法通过socket客户端的source_context和目标的target_context检查SELinux相关权限。

猜你喜欢

转载自blog.csdn.net/qq_36063677/article/details/116750095
今日推荐