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

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

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui