AndroidR 11 系统属性sysprop_library研究及其与SystemProperties的关系(二)

AndroidR 11 系统属性sysprop_library研究及其与SystemProperties的关系(二)

继上篇文章《AndroidR 11 系统属性sysprop_library研究及其与SystemProperties的关系(一)》,我们来验证下遗留的猜测“sysprop文件在编译时会转换成java类文件”的问题,并简述下如何在sysprop中添加自定义系统(或平台)属性。

3、sysprop文件与java类文件的转换

这部分我们尝试寻找下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语言文件。

4、Android R上新增自定义系统属性

由于AndroidR上使用sysprop文件统一管理维护系统属性,而且格式简单明了,所以我们自定义新增系统属性,只需要按照sysprop中的格式照猫画虎就可以了。

但是在新增后重新编译make PlatformProperties时,会报错。我们看下报错信息:
AndroidR 11 系统属性sysprop_library研究及其与SystemProperties的关系(二)_第1张图片
我们搜索下代码,看下此处错误是哪里打印的:
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文件中按照相应的格式添加就可以了。

5、总结

到这里,我们用两篇文章的篇幅应该能够说清楚了Android R上系统属性sysprop_library与SystemProperties的关系。

我们有理由相信,谷歌源码引入这样一种方式,能够更加方便统一的管理维护系统(或平台)属性。

这种对SystemProperties的再次封装和抽象,也是一种很好的设计思想。系统属性的读写是有必要对平台侧或者应用层开发工程师透明的,他们无需关心具体的系统属性名称是什么,更关注的是如何读写系统属性方便、代码可读性好。

由于笔者水平有限,对代码的探究及行文中的遣词造句可能存在谬误,还请同行批评指正。

你可能感兴趣的:(android,java,property)