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

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

相关推荐
我命由我1234513 小时前
Android Process 问题:NoSuchMethodError,No static method myProcessName()
android·java·java-ee·android studio·安卓·android-studio·安卓开发
TroubleMaker15 小时前
OkHttp源码学习之Authenticator
android·java·okhttp
yodala15 小时前
C++中的表达式
android·c++
susu108301891115 小时前
android studio实现圆形图片
android·ide·android studio
hedalei17 小时前
Android13工具修改wifi mac地址以后没有更新的问题
android·wifi·mac地址
Mac Zhu17 小时前
Android中的蓝牙:BLE、经典蓝牙
android
勿忘初心9117 小时前
Android车机DIY开发之学习篇(二)编译Kernel以正点原子为例
android·arm开发·单片机·嵌入式硬件·学习·eclipse
胤胤爸20 小时前
Android ndk-jni语法—— 4
android·java·前端
satadriver20 小时前
android刷机
android
山川而川-R20 小时前
记录一次Android Studio的下载、安装、配置
android·ide·android studio