Physical Address:
ChongQing,China.
WebSite:
Android12编译配置Overlay实现
在Android Framework层有很多配置是通过XML的形式进行设置的,以CarService为例,其原生配置位于packages/services/Car/service/res/values/config.xml文件内,里面通常包含了各个子manager的相关配置信息,这里摘录部分:
< = =></>< =></><string name="config_evsRearviewCameraId" translatable="false">/dev/video0</string> <string name="config_template_activity_class_name"> androidx.car.app.activity.CarAppActivity </string>
在这个配置中,name指代具体的配置名称,string指代其数据类型,而标签内的内容即为该配置对应的值;在这里我们可以看到,默认的config_evsRearviewCameraId配置为/dev/video0;在CarEvsManager内会通过读取该配置信息,从而打开相应的Camera设备,其相关源码如下所示:
() {} {(.);}(!()) {();;}//packages/services/Car/service/src/com/android/car/evs/CarEvsService.java String cameraId; if (mUseCameraIdOverride) { cameraId = mCameraIdOverride; } else { cameraId = mContext.getString(R.string.config_evsRearviewCameraId); } if (!nativeOpenCamera(mNativeEvsServiceObj, cameraId)) { Slog.e(TAG_EVS, "Failed to open a target camera device"); return false; }
在我们实际开发时,与硬件相关的配置往往是多样的,依据硬件设计而变化,此时我们就需要来对这些差异项进行管理,使其能够满足我们多样化的需求。而Google其实也早就考虑到了这一点,并在其编译系统内为我们提供了相关了工具,本篇文章将从实践出发,聊聊其Overlay配置的实现机制。
在面对上面场景时,我们大致要通过以下几个步骤来实现配置管理。
第一步,我们需要在合适的位置创建overlay文件夹,并递归创建与我们需要overlay配置所同名的文件夹,在此示例中为packages/services/Car/service/res/values/,并创建同名的配置文件config.xml 。故此,完整路径为${SELF_DEFINED_DIR}/overlay/packages/services/Car/service/res/values/config.xml;一般我们为自己的board在device目录下创建属于自己平台的文件夹,并将需要overlay的配置放置到该文件夹内。
第二步,编辑config.xml,将我们需要进行替换的配置项写入,比如这里我们将config_evsRearviewCameraId配置值由默认的/dev/video0变为/dev/video12:
< = =></><string name="config_evsRearviewCameraId" translatable="false">/dev/video12</string>
第三步,新建Makefile,一般会与overlay文件夹同级,在该Makefile内,通过PRODUCT_PACKAGE_OVERLAYS或DEVICE_PACKAGE_OVERLAYS关键字来完成替换:
{}DEVICE_PACKAGE_OVERLAYS += ${SELF_DEFINED_DIR}/overlay
完成上述配置之后,我们再进行编译,编译之后得到具体的CarService.apk文件,我们单纯解压该apk文件是无法得到其配置信息的,此时我们可以借助aapt工具来获取apk内的配置信息:
|aapt dump strings CarService.apk | grep “evsRearviewCameraId”
通过这样的方式我们可以直接检验Overlay的配置是否生效;同时我们还会发现,未被替换的配置内容仍旧保持原样,这可以说明overlay并非是简单的文件替换,更像是字段替换,具体的逻辑是如何实现的呢。这里有两个关键的Makefile:
$(((), \$(()/, $()))))($(((), \$(()/, $()))))//build/make/core/package_internal.mk: $(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \ $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR))))) device_package_overlays := $(strip \ $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \ $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))))
$((),\$(((),$()))\$(((),$()))\$())//build/make/core/aapt2.mk: my_overlay_resources_flat := \ $(foreach r, $(my_overlay_resources),\ $(eval o := $(call aapt2-compiled-resource-out-file,$(r),$(my_compiled_res_base_dir)))\ $(eval $(call aapt2-compile-one-resource-file-rule,$(r),$(o)))\ $(o))
这里面涉及到两个自定义的函数:aapt2-compiled-resource-out-file、aapt2-compile-one-resource-file-rule,这两个自定义的函数我们可以在definitions.mk中找到:
#$($((((),$(((1))))))$(2)/$((),/,$())_$( $((())),$(((1))),$((1))).)$(2) : $(1) $()$$()()$() $()() $() $<//build/make/core/definitions.mk # Convert input resource file path to output file path. # values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat; # For other resource file, just replace the last "/" with "_" and # add .flat extension. # # $(1): the input resource file path # $(2): the base dir of the output file path # Returns: the compiled output file path define aapt2-compiled-resource-out-file $(strip \ $(eval _p_w := $(strip $(subst /,$(space),$(dir $(call clean-path,$(1)))))) $(2)/$(subst $(space),/,$(_p_w))_$(if $(filter values%,$(lastword $(_p_w))),$(patsubst %.xml,%.arsc,$(notdir $(1))),$(notdir $(1))).flat) endef # Set up rule to compile one resource file with aapt2. # Must be called with $(eval). # $(1): the source file # $(2): the output file define aapt2-compile-one-resource-file-rule $(2) : $(1) $(AAPT2) @echo "AAPT2 compile $$@ <- $$<" $$(call aapt2-compile-one-resource-file) endef define aapt2-compile-one-resource-file @mkdir -p $(dir $@) $(hide) $(AAPT2) compile -o $(dir $@) $(PRIVATE_AAPT2_CFLAGS) $< endef
[…] 关于Overylay的更多细节,可以参考我的文章,希望对各位有所帮助~ […]