Skytoby

Android SystemProperties系统属性分析

Android SystemProperties系统属性分析

SystemProperties.set方法可以设置系统属性,通过设置系统属性可以启动一些服务和操作,如关机,重启、uncrypt服务等。下面将分析为什么设置系统属性,可以做到即时生效某些操作。

下面将以SystemProperties.set(“ctl.start”, “uncrypt”);为例说明整个流程。

一、属性服务客户端

1.1 SystemProperties.set

[->SystemProperties.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Set the value for the given {@code key} to {@code val}.
*
* @throws IllegalArgumentException if the {@code val} exceeds 91 characters
* @hide
*/
@UnsupportedAppUsage
public static void set(@NonNull String key, @Nullable String val) {
if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
throw new IllegalArgumentException("value of system property '" + key
+ "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
}
if (TRACK_KEY_ACCESS) onKeyAccess(key);
//调用native方法
native_set(key, val);
}

1.2 SystemProperties_set

[->android_os_SystemProperties.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
jstring valJ)
{
auto handler = [&](const std::string& key, bool) {
std::string val;
if (valJ != nullptr) {
ScopedUtfChars key_utf(env, valJ);
val = key_utf.c_str();
}
return android::base::SetProperty(key, val);
};
if (!ConvertKeyAndForward(env, keyJ, true, handler)) {
// Must have been a failure in SetProperty.
jniThrowException(env, "java/lang/RuntimeException",
"failed to set system property");
}
}

1.3 SetProperty

[->properties.cpp]

1
2
3
bool SetProperty(const std::string& key, const std::string& value) {
return (__system_property_set(key.c_str(), value.c_str()) == 0);
}

1.4 system_property_set

[->system_property_set.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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) {
// Old protocol does not support long names or values
if (strlen(key) >= PROP_NAME_MAX) return -1;
if (strlen(value) >= PROP_VALUE_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);
//发送消息,消息中包括key和value
return send_prop_msg(&msg);
} 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;
}
}

1.5 send_prop_msg

[->system_property_set.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
static int send_prop_msg(const prop_msg* msg) {
PropertyServiceConnection connection;
if (!connection.IsValid()) {
return connection.GetLastError();
}

int result = -1;
//连接PROP_SERVICE的socket
int s = connection.socket();

//通过socket发送消息
const int num_bytes = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
if (num_bytes == sizeof(prop_msg)) {
// We successfully wrote to the property server but now we
// wait for the property server to finish its work. It
// acknowledges its completion by closing the socket so we
// poll here (on nothing), waiting for the socket to close.
// If you 'adb shell setprop foo bar' you'll see the POLLHUP
// once the socket closes. Out of paranoia we cap our poll
// at 250 ms.
pollfd pollfds[1];
pollfds[0].fd = s;
pollfds[0].events = 0;
const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) {
result = 0;
} else {
// Ignore the timeout and treat it like a success anyway.
// The init process is single-threaded and its property
// service is sometimes slow to respond (perhaps it's off
// starting a child process or something) and thus this
// times out and the caller thinks it failed, even though
// it's still getting around to it. So we fake it here,
// mostly for ctl.* properties, but we do try and wait 250
// ms so callers who do read-after-write can reliably see
// what they've written. Most of the time.
// TODO: fix the system properties design.
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Property service has timed out while trying to set /"%s/" to /"%s/"",
msg->name, msg->value);
result = 0;
}
}

return result;
}

static const char property_service_socket[] = “/dev/socket/“ PROP_SERVICE_NAME;

这里通过property_service建立socket的连接,将key,value发送到property_service。

那么property_service是在什么时候启动的,是在开机的时候通过init进程启动的服务,开启property_service后,一直在监听property_service_socket,等待客户端的连接。

二、属性服务服务端

2.1 init.main

[->init.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
int main(int argc, char** argv) {

.....
//property初始化,见2.2节
property_init();

// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();

// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();

// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

....
property_load_boot_defaults();
export_oem_lock_status();
//property服务启动,见2.3节
start_property_service();
set_usb_controller();
...
//初始化ActionManager
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();

LoadBootScripts(am, sm);

// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();

am.QueueEventTrigger("early-init");

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");

// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");

// Starting the BoringSSL self test, for NIAP certification compliance.
am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");

// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");

// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}

// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
//循环查询action
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;

if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}
//是否有事件需要处理
if (!(waiting_for_prop || Service::is_exec_service_running())) {
//依次执行每个action中携带command对应的执行函数
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_restart_time = RestartProcesses();

// If there's a process that needs restarting, wake up in time for that.
if (next_process_restart_time) {
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_restart_time - boot_clock::now())
.count();
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
}
// 有action待处理,不等待
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}

//等待子线程终止信号的处理
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
}

2.2 property_init

[->property_service.cpp]

1
2
3
4
5
6
7
8
9
10
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}
if (!property_info_area.LoadDefaultPath()) {
LOG(FATAL) << "Failed to load serialized property info file";
}
}

2.3 start_property_service

[->property_service.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void start_property_service() {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);

property_set("ro.property_service.version", "2");

//创建端口
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
PLOG(FATAL) << "start_property_service socket creation failed";
}

listen(property_set_fd, 8);

//注册epoll handler,处理消息
register_epoll_handler(property_set_fd, handle_property_set_fd);
}

2.4 handle_property_set_fd

[->property_service.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
static void handle_property_set_fd() {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}

ucred cr;
socklen_t cr_size = sizeof(cr);
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
return;
}

SocketConnection socket(s, cr);
uint32_t timeout_ms = kDefaultSocketTimeout;

uint32_t cmd = 0;
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
PLOG(ERROR) << "sys_prop: error while reading command from the socket";
socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}

switch (cmd) {
//处理消息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;

const auto& cr = socket.cred();
std::string error;
uint32_t result =
//见2.5节
HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
<< "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
<< error;
}

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;
}

const auto& cr = socket.cred();
std::string error;
uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
<< "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
<< error;
}
socket.SendUint32(result);
break;
}

default:
LOG(ERROR) << "sys_prop: invalid command " << cmd;
socket.SendUint32(PROP_ERROR_INVALID_CMD);
break;
}
}

2.5 HandlePropertySet

[->property_service.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
if (!IsLegalPropertyName(name)) {
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}

if (StartsWith(name, "ctl.")) {
//检查权限
if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
*error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
//见2.6节
HandleControlMessage(name.c_str() + 4, value, cr.pid);
return PROP_SUCCESS;
}

const char* target_context = nullptr;
const char* type = nullptr;
property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);

if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
*error = "SELinux permission check failed";
return PROP_ERROR_PERMISSION_DENIED;
}

if (type == nullptr || !CheckType(type, value)) {
*error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
(type ?: "(null)"));
return PROP_ERROR_INVALID_VALUE;
}

// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
// 开关机
if (name == "sys.powerctl") {
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
std::string process_cmdline;
std::string process_log_string;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process
// path.
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
}
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
}

if (name == "selinux.restorecon_recursive") {
return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
}
//设置属性
return PropertySet(name, value, error);
}

根据不同的key字段的不同进行处理,在进行处理之前需要是否有相应的权限。

2.6 HandleControlMessage

[->init.cpp]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
const auto& map = get_control_message_map();
const auto it = map.find(msg);

if (it == map.end()) {
LOG(ERROR) << "Unknown control msg '" << msg << "'";
return;
}

std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
std::string process_cmdline;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
std::replace(process_cmdline.begin(), process_cmdline.end(), '/0', ' ');
process_cmdline = Trim(process_cmdline);
} else {
process_cmdline = "unknown process";
}

LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
<< " (" << process_cmdline << ")";

const ControlMessageFunction& function = it->second;

//服务
if (function.target == ControlTarget::SERVICE) {
//通过服务名字获取avc
Service* svc = ServiceList::GetInstance().FindService(name);
if (svc == nullptr) {
LOG(ERROR) << "No such service '" << name << "' for ctl." << msg;
return;
}
//触发启动服务
if (auto result = function.action(svc); !result) {
LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": "
<< result.error();
}

return;
}

if (function.target == ControlTarget::INTERFACE) {
for (const auto& svc : ServiceList::GetInstance()) {
if (svc->interfaces().count(name) == 0) {
continue;
}

if (auto result = function.action(svc.get()); !result) {
LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name()
<< " with interface " << name << ": " << result.error();
}

return;
}

LOG(ERROR) << "Could not find service hosting interface " << name;
return;
}

LOG(ERROR) << "Invalid function target from static map key '" << msg
<< "': " << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
}

这里最终通过function.action来启动uncrypt服务。

附录

源码路径

1
2
3
4
5
6
frameworks/base/core/java/android/os/SystemProperties.java
frameworks/base/core/jni/android_os_SystemProperties.cpp
system/core/base/properties.cpp
bionic/libc/bionic/system_property_set.cpp
system/core/init/property_service.cpp
system/core/init/init.cpp