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

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

相关推荐
Libraeking3 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位3 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen1235 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs6 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob6 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔6 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei9966 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly8 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首8 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
消失的旧时光-194311 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed