Android10 Framework—Init进程-11.客户端操作属性

本章看看客户端是如何去操作属性的,这里主要分为 2 个部分:

  • 服务端中属性的初始化过程由init完成,那客户端属性初始化是哪里做的呢
  • 客户端如何发起属性操作

客户端属性初始化

客户端对属性初始化是由__system_properties_init()函数完成,其调用路径如下:

c 复制代码
// bionic/libc/bionic/libc_init_dynamic.cpp

// We flag the __libc_preinit function as a constructor to ensure that
// its address is listed in libc.so's .init_array section.
// This ensures that the function is called by the dynamic linker as
// soon as the shared library is loaded.
// We give this constructor priority 1 because we want libc's constructor
// to run before any others (such as the jemalloc constructor), and lower
// is better (http://b/68046352).
__attribute__((constructor(1))) static void __libc_preinit() {
  // The linker has initialized its copy of the global stack_chk_guard, and filled in the main
  // thread's TLS slot with that value. Initialize the local global stack guard with its value.
  __stack_chk_guard = reinterpret_cast<uintptr_t>(__get_tls()[TLS_SLOT_STACK_GUARD]);

  __libc_preinit_impl();
}

__attribute__((noinline))
static void __libc_preinit_impl() {
  ...省略代码
  
  __libc_init_common();
  
  ...省略代码
}

// bionic/libc/bionic/libc_init_common.cpp
void __libc_init_common() {
  ...省略代码

  __system_properties_init(); // Requires 'environ'.
  
  ...省略代码
}

我看看重点看看__libc_preinit函数的注释

c 复制代码
// We flag the __libc_preinit function as a constructor to ensure that
// its address is listed in libc.so's .init_array section.
// This ensures that the function is called by the dynamic linker as
// soon as the shared library is loaded.
// We give this constructor priority 1 because we want libc's constructor
// to run before any others (such as the jemalloc constructor), and lower
// is better (http://b/68046352).

__libc_preinit函数会被放到libc.so的init_array段中,这样就确保了共享库被加载时能尽可能快的调用__libc_preinit;constructor被设置为 1,这就意味着__libc_preinit被执行的优先级会非常高(值越低执行优先级越高)

总结:

这样应用程序就完成了属性的初始化。接下来看看__system_properties_init

c 复制代码
// bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_properties_init() {
  return system_properties.Init(PROP_FILENAME) ? 0 : -1;
}

看看又调用了system_property_api.cpp这个API文件中,里面最后还是调用我们前面说的大管家类SystemProperties。

c 复制代码
bool SystemProperties::Init(const char* filename) {
  // This is called from __libc_init_common, and should leave errno at 0 (http://b/37248982).
  ErrnoRestorer errno_restorer;

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

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

  if (is_dir(property_filename_)) {
    if (access("/dev/__properties__/property_info", R_OK) == 0) {
      contexts_ = new (contexts_data_) ContextsSerialized();
      if (!contexts_->Initialize(false, property_filename_, nullptr)) {
        return false;
      }
    } else {
      contexts_ = new (contexts_data_) ContextsSplit();
      if (!contexts_->Initialize(false, property_filename_, nullptr)) {
        return false;
      }
    }
  } else {
    contexts_ = new (contexts_data_) ContextsPreSplit();
    if (!contexts_->Initialize(false, property_filename_, nullptr)) {
      return false;
    }
  }
  initialized_ = true;
  return true;
}

这个contexts_->Initialize方法我们在"服务端属性文件创建和mmap映射"这篇文章中详细分析过,它完成如下功能:

  • 加载"/dev/properties/property_info"文件的内容(前一章属性安全上下文序列化的内容)
  • 创建ContextNode数组
  • 创建属性文件,并mmap映射属性文件,地址保存到ContextNode中

这个过程其实就是在客户端进程初始化构建了橙色框中的数据结构,后续可以通过属性名查找到prop_area对象,进而读取到属性值。唯一需要注意的是,客户端进程只能读属性不能修改,所以第一个参数设置的false,标识prop_area映射的内存控件只读。

c 复制代码
contexts_->Initialize(false, property_filename_, nullptr)

而init进程设置的是true,是可以写的

c 复制代码
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
  ...省略代码

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

  ...省略代码
  
  return true;
}

属性操作

客户端可以通过如下代码操作属性

c 复制代码
// system/core/libcutils/include/cutils/properties.h
// system/core/libcutils/properties.cpp

#include <cutils/properties.h>

int property_set(const char *key, const char *value)
int property_get(const char *key, char *value, const char *default_value)

读属性

c 复制代码
int property_get(const char *key, char *value, const char *default_value) {
    int len = __system_property_get(key, value);
    if (len > 0) {
        return len;
    }
    if (default_value) {
        len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);
        memcpy(value, default_value, len);
        value[len] = '\0';
    }
    return len;
}

// bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_get(const char* name, char* value) {
  return system_properties.Get(name, value);
}

property_get最后还是调用到大管家SystemProperties中的Get方法

c 复制代码
int SystemProperties::Get(const char* name, char* value) {
  const prop_info* pi = Find(name);

  if (pi != nullptr) {
    return Read(pi, nullptr, value);
  } else {
    value[0] = 0;
    return 0;
  }
}

Get方法还是先通过属性名找到prop_info,然后读取里面的属性。

写属性

c 复制代码
int property_set(const char *key, const char *value) {
    return __system_property_set(key, value);
}

前面看到调用的很多方法都是在system_property_api.cpp中,property_set有所不同,它调用的是system_property_set.cpp中方法

c 复制代码
// 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) {
    detect_protocol_version();
  }

  if (g_propservice_protocol_version == kProtocolVersion1) {//旧版本
    ....省略代码
  } else {//走这里新版本
    // 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;
    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);
    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) {
      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;
  }
}
  • PropertyServiceConnection构造函数创建socket,并连接"/dev/socket/property_service"
  • 然后调用如下代码发送请求
c 复制代码
writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()

到这里客户端的写操作也就玩成了。

相关推荐
雨白3 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk3 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING4 小时前
RN容器启动优化实践
android·react native
恋猫de小郭6 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
YF021110 小时前
AndroidStudio工具链配置
android studio
Kapaseker11 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴12 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭1 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack