继上篇文章《AndroidR 11 系统属性sysprop_library研究及其与SystemProperties的关系(一)》,我们来验证下遗留的猜测“sysprop文件在编译时会转换成java类文件”的问题,并简述下如何在sysprop中添加自定义系统(或平台)属性。
这部分我们尝试寻找下sysprop文件编译后转换成的java类文件的位置,如果猜测正确的话。
从设计的角度,sysprop文件的格式是更方便阅读与编辑的;从编译的角度,代码逻辑需要符合java类语言规范。所以必须添加一层转换机制,类似于android平台特有的将AIDL文件在编译过程中转换成java类文件的机制。
我们尝试搜索下“sysprop_library”相关的字段,可以发现编译控制文件:build/soong/sysprop/sysprop_library.go
摘取一段关键代码如下:
func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
var checkApiFileTimeStamp android.WritablePath
ctx.VisitDirectDeps(func(dep android.Module) {
if m, ok := dep.(*syspropLibrary); ok {
checkApiFileTimeStamp = m.checkApiFileTimeStamp
}
})
for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
ctx.Build(pctx, android.BuildParams{
Rule: syspropJava,
Description: "sysprop_java " + syspropFile.Rel(),
Output: srcJarFile,
Input: syspropFile,
Implicit: checkApiFileTimeStamp,
Args: map[string]string{
"scope": g.properties.Scope,
},
})
g.genSrcjars = append(g.genSrcjars, srcJarFile)
}
}
从上面的编译逻辑中可以窥知,编译过程中会输出srcJarFile类型的文件。那我们去编译生成的中间文件路径下查找一下。
发现在路径下out\soong\.intermediates\system\libsysprop\srcs\PlatformProperties_java_gen\gen\sysprop\system\libsysprop\srcs\android\sysprop\TelephonyProperties.srcjar
确实存在srcjar的文件。
同样的,我们将文件后缀修改为.zip并解压,然后我们终于看到了我们翘首以盼的TelephonyProperties.java文件。
打开该文件可以看到,跟我们通过反编译TelephonyProperties.class文件得到的代码逻辑一模一样,此处不再截取赘述。
到这里,我们验证了自己的想法,找到了由编译工具自动将sysprop文件转换生成的java语言文件。
由于AndroidR上使用sysprop文件统一管理维护系统属性,而且格式简单明了,所以我们自定义新增系统属性,只需要按照sysprop中的格式照猫画虎就可以了。
但是在新增后重新编译make PlatformProperties
时,会报错。我们看下报错信息:
我们搜索下代码,看下此处错误是哪里打印的:
build/soong/sysprop/sysprop_library.go
中:
func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
baseModuleName := m.BaseModuleName()
for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) {
if syspropFile.Ext() != ".sysprop" {
ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
}
}
if ctx.Failed() {
return
}
m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-current.txt")
m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
// dump API rule
rule := android.NewRuleBuilder()
m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt")
rule.Command().
BuiltTool(ctx, "sysprop_api_dump").
Output(m.dumpedApiFile).
Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
rule.Build(pctx, ctx, baseModuleName+"_api_dump", baseModuleName+" api dump")
// check API rule
rule = android.NewRuleBuilder()
// 1. current.txt <-> api_dump.txt
msg := fmt.Sprintf(`\n******************************\n`+
`API of sysprop_library %s doesn't match with current.txt\n`+
`Please update current.txt by:\n`+
`m %s-dump-api && rm -rf %q && cp -f %q %q\n`+
`******************************\n`, baseModuleName, baseModuleName,
m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String())
rule.Command().
Text("( cmp").Flag("-s").
Input(m.dumpedApiFile).
Input(m.currentApiFile).
Text("|| ( echo").Flag("-e").
Flag(`"` + msg + `"`).
Text("; exit 38) )")
// 2. current.txt <-> latest.txt
msg = fmt.Sprintf(`\n******************************\n`+
`API of sysprop_library %s doesn't match with latest version\n`+
`Please fix the breakage and rebuild.\n`+
`******************************\n`, baseModuleName)
rule.Command().
Text("( ").
BuiltTool(ctx, "sysprop_api_checker").
Input(m.latestApiFile).
Input(m.currentApiFile).
Text(" || ( echo").Flag("-e").
Flag(`"` + msg + `"`).
Text("; exit 38) )")
m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp")
rule.Command().
Text("touch").
Output(m.checkApiFileTimeStamp)
rule.Build(pctx, ctx, baseModuleName+"_check_api", baseModuleName+" check api")
}
可以看到在编译过程中会生成out\soong\.intermediates\system\libsysprop\srcs\PlatformProperties_sysprop_library\api-dump.txt
,同时会跟源代码目录system\libsysprop\srcs\api\
下的文件PlatformProperties-current.txt和PlatformProperties-latest.txt
进行校验检查。
所以,我们知道了是由于我们新修改了sysprop文件,此时生成的api-dump.txt
跟源码中的PlatformProperties-current.txt
不一致,打印出了此错误。
在这里贴一下PlatformProperties-current.txt
文件中的内容,其实就是针对PlatformProperties
模块中所有的sysprop
文件做了一个整理打包:
props {
module: "android.sysprop.AdbProperties"
prop {
api_name: "secure"
access: Writeonce
scope: Internal
prop_name: "ro.adb.secure"
}
}
props {
module: "android.sysprop.ApkVerityProperties"
prop {
api_name: "apk_verity_mode"
type: Integer
access: Writeonce
prop_name: "ro.apk_verity.mode"
}
}
... ...
props {
module: "android.sysprop.PowerProperties"
prop {
api_name: "fixed_performance_scale_factor"
type: Integer
prop_name: "ro.power.fixed_performance_scale_factor"
}
}
props {
module: "android.sysprop.TelephonyProperties"
prop {
api_name: "airplane_mode_on"
access: ReadWrite
scope: Internal
prop_name: "persist.radio.airplane_mode_on"
integer_as_bool: true
}
prop {
api_name: "icc_operator_alpha"
type: StringList
access: ReadWrite
scope: Internal
prop_name: "gsm.sim.operator.alpha"
}
prop {
api_name: "multi_sim_config"
type: String
access: ReadWrite
prop_name: "persist.radio.multisim.config"
}
}
... ...
输出该错误的同时也输出了解决的方法,在源码根目录下运行下面的命令即可解决该错误:
m PlatformProperties-dump-api && rm -rf system/libsysprop/srcs/api/PlatformProperties-current.txt && cp -f out/soong/.intermediates/system/libsysprop/srcs/PlatformProperties_sysprop_library/api-dump.txt system/libsysprop/srcs/api/PlatformProperties-current.txt
该命令从字面上可以理解为,需要将编译过程中新的api-dump.txt
替换更新到源码中的PlatformProperties-current.txt
文件。
同时我们在源码中查找到这样一个脚本:/build/soong/scripts/freeze-sysprop-api-files.sh
/build/soong/scripts/freeze-sysprop-api-files.sh
# This script freezes APIs of a sysprop_library after checking compatibility
# between latest API and current API.
#
# Usage: freeze-sysprop-api-files.sh <modulePath> <moduleName>
#
# <modulePath>: the directory, either relative or absolute, which holds the
# Android.bp file defining sysprop_library.
#
# <moduleName>: the name of sysprop_library to freeze API.
#
# Example:
# $ . build/envsetup.sh && lunch aosp_arm64-user
# $ . build/soong/scripts/freeze-sysprop-api-files.sh \
# system/libsysprop/srcs PlatformProperties
if [[ -z "$1" || -z "$2" ]]; then
echo "usage: $0 " >&2
exit 1
fi
api_dir=$1/api
m "$2-check-api" && cp -f "${api_dir}/$2-current.txt" "${api_dir}/$2-latest.txt"
该脚本跟上面的命令一样,笔者猜测应该是源码全编译的时候针对sysprop_library
进行校验和api更新的。
运行完上面的命令,应该就没有其他的错误了。所以新增系统(平台)属性在sysprop文件中按照相应的格式添加就可以了。
到这里,我们用两篇文章的篇幅应该能够说清楚了Android R上系统属性sysprop_library与SystemProperties的关系。
我们有理由相信,谷歌源码引入这样一种方式,能够更加方便统一的管理维护系统(或平台)属性。
这种对SystemProperties的再次封装和抽象,也是一种很好的设计思想。系统属性的读写是有必要对平台侧或者应用层开发工程师透明的,他们无需关心具体的系统属性名称是什么,更关注的是如何读写系统属性方便、代码可读性好。
由于笔者水平有限,对代码的探究及行文中的遣词造句可能存在谬误,还请同行批评指正。