安卓系统属性persist类型prop深入剖析
背景:
近来学员朋友在群里问道了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 serviceset_prop(platform_app, test_boot_reason_prop)
+set_prop(platform_app, system_prop)# allow platform apps to read keyguard.no_require_simget_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 serviceset_prop(platform_app, test_boot_reason_prop)
+set_prop(platform_app, system_prop)# allow platform apps to read keyguard.no_require_simget_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详细代码和资料参考如下链接
投屏专题部分:
https://mp.weixin.qq.com/s/IGm6VHMiAOPejC_H3N_SNg
hal+perfetto+surfaceflinger
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
其他课程七件套专题:
点击这里
https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw
视频试看:
https://www.bilibili.com/video/BV1wc41117L4/
参考相关链接:
https://blog.csdn.net/zhimokf/article/details/137958615
更多framework假威风耗:androidframework007