Physical Address:
ChongQing,China.
WebSite:
在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中找到:
//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的更多细节,可以参考我的文章,希望对各位有所帮助~ […]