上层接口实现
system/core/libcutils/properties.cpp:
property_set/property_get:
#include <sys/_system_properties.h>
int property_set(const char *key, const char *value) {
return __system_property_set(key, value);
}
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;
}
可以看到实际上上述两个接口会调用__system_property_set/__system_property_get这两个函数来实现的,再进一步跟下去看看发生了什么?这两个函数的实现是在bionic库中,bionic/libc/bionic/system_properties.cpp:
初始化函数:
int __system_property_area_init() {
free_and_unmap_contexts();
mkdir(property_filename, S_IRWXU | S_IXGRP | S_IXOTH);
if (!initialize_properties()) {
return -1;
}
bool open_failed = false;
bool fsetxattr_failed = false;
list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
if (!l->open(true, &fsetxattr_failed)) {
open_failed = true;
}
});
if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
free_and_unmap_contexts();
return -1;
}
initialized = true;
return fsetxattr_failed ? -2 : 0;
}
在初始化函数中,会执行map_system_property_area,这个函数就会去打开设备文件/dev/__properties__properties_serial,并mmap到指针__system_property_area__上。后续只需要针对__system_property_area__这个全局指针操作就可以设置和读取对应property。通过初始化我们也可以看出,property的实现是需要kernel底层支持的,需要实现对应的设备文件和维护property存储区域。
查找函数:
const prop_info* __system_property_find(const char* name) {
if (!__system_property_area__) {
return nullptr;
}
prop_area* pa = get_prop_area_for_name(name);
if (!pa) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name);
return nullptr;
}
return pa->find(name);
}
由上面的init初始化过程可知,最终我们获取到了__system_property_area__这个存储property的区域指针,那么如果我们想要操作一个特定的property,就首先要找到该property对应的存储地址,此find函数就是实现通过name查找对应存储地址的功能。
读取函数:
int __system_property_read(const prop_info* pi, char* name, char* value) {
while (true) {
uint32_t serial = __system_property_serial(pi); // acquire semantics
size_t len = SERIAL_VALUE_LEN(serial);
memcpy(value, pi->value, len + 1);
// TODO: Fix the synchronization scheme here.
// There is no fully supported way to implement this kind
// of synchronization in C++11, since the memcpy races with
// updates to pi, and the data being accessed is not atomic.
// The following fence is unintuitive, but would be the
// correct one if memcpy used memory_order_relaxed atomic accesses.
// In practice it seems unlikely that the generated code would
// would be any different, so this should be OK.
atomic_thread_fence(memory_order_acquire);
if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
if (name != nullptr) {
size_t namelen = strlcpy(name, pi->name, PROP_NAME_MAX);
if (namelen >= PROP_NAME_MAX) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"The property name length for \"%s\" is >= %d;"
" please use __system_property_read_callback"
" to read this property. (the name is truncated to \"%s\")",
pi->name, PROP_NAME_MAX - 1, name);
}
}
return len;
}
}
}
int __system_property_get(const char* name, char* value) {
const prop_info* pi = __system_property_find(name);
if (pi != 0) {
return __system_property_read(pi, nullptr, value);
} else {
value[0] = 0;
return 0;
}
}
经过前面我们知道,利用find函数我们可以找到了property对应的地址,在__system_property_get的实现中,会先通过name调用find函数查找到对应地址,然后把该地址传递给__system_property_read,进一步读取到property的值。
写入函数:
int __system_property_set(const char* key, const char* value) {
if (key == nullptr) return -1;
if (value == nullptr) value = "";
if (strlen(value) >= PROP_VALUE_MAX) return -1;
if (g_propservice_protocol_version == 0) {
detect_protocol_version();
}
if (g_propservice_protocol_version == kProtocolVersion1) {
// Old protocol does not support long names
if (strlen(key) >= PROP_NAME_MAX) return -1;
prop_msg msg;
memset(&msg, 0, sizeof msg);
msg.cmd = PROP_MSG_SETPROP;
strlcpy(msg.name, key, sizeof msg.name);
strlcpy(msg.value, value, sizeof msg.value);
return send_prop_msg(&msg);
} else {
// 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;
}
}
__system_property_set的实现相对于__system_property_get要更加复杂,因为要保证写入权限的安全,所以要受SElinux的限制,从上面的代码可以看到,实际上会利用socket进程间通信方式与Property Service进行交互,并最终由Property Service完成SElinux权限检查,并最终进行设置动作。上面有写到两种通信protocol,第一种会发送PROP_MSG_SETPROP命令码,第二种会发送PROP_MSG_SETPROP2命令码,我们会进一步跟一下看看这个命令到底是做什么。
property service服务的实现代码在如下文件中:
system/core/init/property_service.cpp:
static void handle_property_set_fd() {
......
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;
handle_property_set(socket, prop_value, prop_value, true);
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;
}
handle_property_set(socket, name, value, false);
break;
Property Service类似与一个普通的网络通信服务实现一样,利用socket API来实现,只不过使用的是是UNIX socket:AF_LOCAL(也就是AF_UNIX)。它listen(监听)socket并接收解包客户端发来的请求。这里看到上面的两个命令码实际上是会通过handle_property_set来进行处理的。
handle_property_set --> check_mac_perms (检测SElinux权限)
PropertySetImpl (设置Property实例)-->__system_property_find
__system_property_update/__system_property_add