安卓系统属性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 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详细代码和资料参考如下链接
投屏专题部分:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值