RK3568 Android系统客制化动态替换ro任意属性

简介: RK3568 Android系统客制化动态替换ro任意属性

在Android系统开发中,经常会遇到需要动态修改系统属性的情况。尤其是对于那些标记为只读的属性,修改它们可能会比较困难。本文将以RK3568 Android11为例,介绍如何动态替换任意的ro属性以及在此过程中遇到的问题和解决方案。

需求背景

在Android系统中,属性经常用于配置信息,例如设备型号、硬件版本等。有时,为了适应不同的硬件或软件需求,我们可能需要在系统运行时动态修改这些属性,而不是重新编译整个系统。

方案思路

我的解决方案是在系统启动时,加载一个自定义的属性文件,该文件中的属性将覆盖系统中已存在的属性。这样,我们就可以在不修改系统源代码的情况下,动态地修改任意属性。

Android 11的Property加载流程

  1. 启动init进程:Android系统启动时,首先启动的是init进程。这是系统的第一个进程,负责初始化系统环境和启动其他系统服务。
  2. 加载默认属性init进程首先加载/system/etc/prop.default文件中的属性。这些属性通常包括硬件和系统的基本配置。
if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
    // Try recovery path
    if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
        // Try legacy path
        load_properties_from_file("/default.prop", nullptr, &properties);
    }
}
  1. 加载其他属性文件:除了默认属性文件,init进程还会加载其他多个属性文件,如/system/build.prop/vendor/build.prop等。这些文件中的属性可以覆盖默认属性文件中的属性。
load_properties_from_file("/system/build.prop", nullptr, &properties);
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
  1. 加载设备特定属性:为了支持多种硬件配置,Android 11引入了设备特定的属性文件,如/odm/build.prop/product/build.prop等。这些文件中的属性可以根据具体的硬件配置进行定制。
load_properties_from_file("/odm/build.prop", nullptr, &properties);
load_properties_from_file("/product/build.prop", nullptr, &properties);
  1. 属性覆盖:在加载属性时,后加载的属性会覆盖先加载的属性。这意味着,如果多个属性文件中都定义了同一个属性,那么最后加载的文件中的属性值将被使用。

为什么自定义属性可以覆盖默认属性?

这是因为Android的属性加载机制是基于后加载的属性覆盖先加载的属性的原则设计的。这种设计允许开发者和OEM厂商在不修改系统源代码的情况下,通过添加或修改属性文件来定制系统配置。

例如,我们可以在/product/customize.prop中定义一个属性ro.product.system.model=rk3568_custom。由于这个文件是在其他属性文件之后加载的,所以这个属性会覆盖在之前的文件中定义的同名属性。

实现步骤:

  1. 选择自定义属性文件的位置:考虑到系统的启动顺序和分区挂载情况,我们选择将自定义属性文件放在/product分区,因为这个分区在系统的早期启动阶段就已经被挂载,所以init进程可以在启动时访问它。我们将自定义属性文件命名为customize.prop
  2. 修改属性加载代码:在system/core/init/property_service.cpp文件中,找到PropertyLoadBootDefaults函数。在该函数中,我们在加载其他属性文件之后,添加了加载我们的自定义属性文件的代码。
void PropertyLoadBootDefaults() {
    // TODO(b/117892318): merge prop.default and build.prop files into one
    // We read the properties and their values into a map, in order to always allow properties
    // loaded in the later property files to override the properties in loaded in the earlier
    // property files, regardless of if they are "ro." properties or not.
    std::map<std::string, std::string> properties;
    if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
        // Try recovery path
        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
            // Try legacy path
            load_properties_from_file("/default.prop", nullptr, &properties);
        }
    }
    load_properties_from_file("/system/build.prop", nullptr, &properties);
    load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
        load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
    } else {
        load_properties_from_file("/odm/default.prop", nullptr, &properties);
        load_properties_from_file("/odm/build.prop", nullptr, &properties);
    }
    load_properties_from_file("/product/build.prop", nullptr, &properties);
    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
    if (access(kDebugRamdiskProp, R_OK) == 0) {
        LOG(INFO) << "Loading " << kDebugRamdiskProp;
        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
    }
    // 加载自定义的prop文件 start
    //const char* customPropPath = "/mnt/private/customize.prop";
    const char* customPropPath = "/product/customize.prop";
    if (access(customPropPath, R_OK) == 0) {
        LOG(INFO) << "[custom_prop] Loading " << customPropPath;
        load_properties_from_file(customPropPath, nullptr, &properties);
    } else {
        LOG(INFO) << customPropPath << " not found, skipping.[custom_prop]";
    }
    // 加载自定义的prop文件  end
    for (const auto& [name, value] : properties) {
        std::string error;
        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
            LOG(ERROR) << "Could not set '" << name << "' to '" << value
                       << "' while loading .prop files" << error;
        }
    }
    property_initialize_ro_product_props();
    property_derive_build_fingerprint();
    update_sys_usb_config();
}

如何测试:

我们在customize.prop文件中设置了ro.product.system.model=rk3568_custom。启动系统后,我们使用getprop ro.product.system.model命令,确实得到了rk3568_custom,这证明我们的修改是成功的。

  1. 属性验证:使用getprop命令验证属性是否已被正确加载。
  2. 系统功能测试:确保修改后的系统可以正常启动,并运行各种应用程序。
  3. 日志检查:通过logcat命令,检查系统日志,确保没有与属性加载相关的错误或警告。

遇到的问题:

  1. 分区挂载问题:最初,我们尝试将自定义属性文件放在/mnt/private分区。但在系统启动早期,该分区尚未挂载,导致init进程无法访问。因此,我们选择了/product分区。
  2. 属性覆盖问题:由于属性加载的顺序,后加载的属性会覆盖先加载的属性。因此,我们确保自定义属性文件是在其他属性文件之后加载的。

结论

通过上述方法,我们成功地实现了在RK3568 Android12系统中动态替换任意的ro属性。这为Android系统开发提供了更大的灵活性,使我们能够更容易地适应不同的硬件和配置需求。

相关文章
|
1天前
|
存储 监控 调度
Android系统服务:WMS、AMS相关知识
参考文献 Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析 Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析 Android窗口管理服务WindowManagerService对输入法窗口(Input Method Window)的管理分析 Android窗口管理服务WindowManagerService显示窗口动画的原理分析
|
5天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
5 0
|
5天前
|
Java Linux Android开发
Android面试题之说说系统的启动流程(总结)
这篇文章概述了Android系统的启动流程,从Boot Rom到Zygote进程和SystemServer的启动。init进程作为用户级别的第一个进程,负责创建文件目录、初始化服务并启动Zygote。Zygote通过预加载资源和创建Socket服务,使用fork函数生成SystemServer进程。fork过程中,子进程继承父进程大部分信息但具有独立的进程ID。Zygote预加载资源以减少后续进程的启动时间,而SystemServer启动众多服务并最终开启Launcher应用。文中还讨论了为何从Zygote而非init或SystemServer fork新进程的原因。
13 2
|
12天前
|
前端开发 Java API
Android系统中读写和显示图片
Android系统中读写和显示图片
10 0
|
12天前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
22 2
|
15天前
|
Java 机器人 Linux
01. 【Android教程】系统背景及结构概述
01. 【Android教程】系统背景及结构概述
8 0
|
20天前
|
运维 监控 Android开发
构建高效自动化运维系统的策略与实践构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第29天】随着信息技术的迅猛发展,企业IT基础设施变得日益复杂,传统的手动运维模式已难以满足高效率、高稳定性的要求。本文将深入探讨如何通过自动化工具和策略来构建一个高效的自动化运维系统。文中不仅分析了自动化运维的必要性,还详细介绍了实现过程中的关键步骤,包括监控、配置管理、故障响应等,并结合实际案例分析其效果,以期为读者提供一套行之有效的自动化运维解决方案。
|
Java 调度 Android开发
android体系课-系统启动流程-之zygote进程启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己
|
Java 调度 Android开发
android体系课-系统启动流程-之SystemServer启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己