背景:
近来学员朋友在群里问道了prop属性值进行持久化存储相关的问题,针对prop大部分情况下都是在代码端进行get获取读取操作,因为很多系统属性都是ro类型的,即不可以修改的,有一些debug可以修改的属性,但修改重启又变成了空,所以有是否可以持久化存储的prop,并且可读可写这种,那么就是今天要讲解的persist属性
persist属性源码分析:
源码位置:
system/core/init/property_service.cpp
static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value,
SocketConnection* socket, std::string* error) {
//省略
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) {
(1)
if (persist_write_thread) {
persist_write_thread->Write(name, value, std::move(*socket));(2)
return {
};
}
WritePersistentProperty(name, value);
}
NotifyPropertyChange(name, value);
return {
PROP_SUCCESS};
}
上面代码(1)就会判断setprop的key是不是带了persist开头的属性,如果是persist开头,接下来判断persist_write_thread线程是否不为null,如果不为null则调用persist_write_thread->Write进行持久化写入。
void PersistWriteThread::Write(std::string name, std::string value, SocketConnection socket) {
{
std::unique_lock<std::mutex> lock(mutex_);
work_.emplace_back(std::move(name), std::move(value), std::move(socket));
}
cv_.notify_all();//唤醒操作
}
void PersistWriteThread::Work() {
while (true) {
std::tuple<std::string, std::string, SocketConnection> item;
// Grab the next item within the lock.
{
std::unique_lock<std::mutex> lock(mutex_);
while (work_.empty()) {
//一直循环判断队列是否为空
cv_.wait(lock);
}
item = std::move(work_.front());
work_.pop_front();
}
std::this_thread::sleep_for(1s);
// Perform write/fsync outside the lock.
WritePersistentProperty(std::get<0>(item), std::get<1>(item));//写入到文件
NotifyPropertyChange(std::get<0>(item), std::get<1>(item));
SocketConnection& socket = std::get<2>(item);
socket.SendUint32(PROP_SUCCESS);
}
}
可以看到最后是在独立线程中调用WritePersistentProperty进行写入属性到文件
// Persistent properties are not written often, so we rather not keep any data in memory and read
// then rewrite the persistent property file for each update.
void WritePersistentProperty(const std::string& name, const std::string& value) {
//省略
if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) {
//真正写入到文件
LOG(ERROR) << "Could not store persistent property: " << result.error();
}
}
这里又是调用WritePersistentPropertyFile写入到文件
Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
const std::string temp_filename = persistent_property_filename + ".tmp";
unique_fd fd(TEMP_FAILURE_RETRY(
open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
std::string serialized_string;
if (!persistent_properties.SerializeToString(&serialized_string)) {
return Error() << "Unable to serialize properties";
}
if (!WriteStringToFd(serialized_string, fd)) {
return ErrnoError() << "Unable to write file contents";
}
fsync(fd.get());
fd.reset();
if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
int saved_errno = errno;
unlink(temp_filename.c_str());
return Error(saved_errno) << "Unable to rename persistent property file";
}
// rename() is atomic with regards to the kernel's filesystem buffers, but the parent
// directories must be fsync()'ed otherwise, the rename is not necessarily written to storage.
// Note in this case, that the source and destination directories are the same, so only one
// fsync() is required.
auto dir = Dirname(persistent_property_filename);
auto dir_fd = unique_fd{
open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};
if (dir_fd < 0) {
return ErrnoError() << "Unable to open persistent properties directory for fsync()";
}
fsync(dir_fd.get());
return {
};
}
这里的文件路径是
std::string persistent_property_filename = "/data/property/persistent_properties";
所以persist的属性最后都可以去/data/property/persistent_properties查看
不过经过了序列化了,可读性不是那么好。
实战过程:
实战主要有以下几个地方修改
1、代码定义和写入
这里需要system_server进程和app进程都可以进行写入
system_server进行写入
系统app写入
2、selinux部分
这个selinux部分是最核心的部分,一般写新的persist最好不要完全自己新定义一个prop,完全可以采用报现在有的prop已经定义好的selinux的大腿,这里因为persist.sys本身就是被定义好了相关的标签:
./prebuilts/api/33.0/private/property_contexts:72:persist.sys. u:object_r:system_prop:s0
可以看到只要是persist.sys. 开头的都是定义为了system_prop,所以system_server进程写入数据等都不需要额外编写,因为直接有的。
./private/system_server.te:701:set_prop(system_server, system_prop)
唯一要额外编写的就是针对系统app这种,这个就需要额外写一点selinux,这里主要针对platform_app这种签名app
private/platform_app.te
diff --git a/private/platform_app.te b/private/platform_app.te
index 44de21c25..43d398b04 100644
--- a/private/platform_app.te
+++ b/private/platform_app.te
@@ -106,6 +106,7 @@ allow platform_app system_server:udp_socket {
# allow platform apps to connect to the property service
set_prop(platform_app, test_boot_reason_prop)
+set_prop(platform_app, system_prop)
# allow platform apps to read keyguard.no_require_sim
get_prop(platform_app, keyguard_config_prop)
test@test:~/disk2/nx563j_aosp14/system/sepolicy
prebuilts/api/34.0/private/platform_app.te
diff --git a/prebuilts/api/34.0/private/platform_app.te b/prebuilts/api/34.0/private/platform_app.te
index 44de21c25..43d398b04 100644
--- a/prebuilts/api/34.0/private/platform_app.te
+++ b/prebuilts/api/34.0/private/platform_app.te
@@ -106,6 +106,7 @@ allow platform_app system_server:udp_socket {
# allow platform apps to connect to the property service
set_prop(platform_app, test_boot_reason_prop)
+set_prop(platform_app, system_prop)
# allow platform apps to read keyguard.no_require_sim
get_prop(platform_app, keyguard_config_prop)
验证如下:
可以看出reboot后persist.sys.test.value1的值还是value-2
看看data/property/persistent_properties下面是否有保存
在底部确实存在persist.sys.test.value1为value-2的prop
更多framework技术干货,请关注下面“千里马学框架”