Android13 automotive移植
需求背景
目前Google已经释放Android 13的基线代码,其中包括了automotive的product,但芯片厂商针对automotive的Android 13基线并未释放,在释放前,下面的方案商无法得知芯片厂商的将会以哪个product来释放automotive基线,因此,需要提前预研在其他product上移植automotive功能。
相关概念
-
automotive
翻译过来就是汽车的意思,它是基于Android平台的车载信息娱乐系统,简称IVI(In-Vehicle Infotainment),在Android 13中,automotive长这样
-
product
AOSP针对不同的设备,设置的不同的产品。我们在编译源码时,执行
lunch
命令后展示的列表就是当前基线提供的所有productq@jason-OptiPlex-7080:/projects/AOSP_Android13$ source build/envsetup.sh q@jason-OptiPlex-7080:/projects/AOSP_Android13$ lunch You're building on Linux Lunch menu .. Here are the common combinations: 1. aosp_arm-eng 2. aosp_arm64-eng 3. aosp_barbet-userdebug 4. aosp_bluejay-userdebug 5. aosp_bluejay_car-userdebug 6. aosp_bramble-userdebug 7. aosp_bramble_car-userdebug 8. aosp_car_arm-userdebug 9. aosp_car_arm64-userdebug 10. aosp_car_x86-userdebug 11. aosp_car_x86_64-userdebug 12. aosp_cf_arm64_auto-userdebug 13. aosp_cf_arm64_phone-userdebug 14. aosp_cf_x86_64_foldable-userdebug 15. aosp_cf_x86_64_pc-userdebug 16. aosp_cf_x86_64_phone-userdebug 17. aosp_cf_x86_64_tv-userdebug 18. aosp_cf_x86_auto-userdebug 19. aosp_cf_x86_phone-userdebug 20. aosp_cf_x86_tv-userdebug 21. aosp_cheetah-userdebug 22. aosp_cloudripper-userdebug 23. aosp_coral-userdebug 24. aosp_coral_car-userdebug 25. aosp_flame-userdebug 26. aosp_flame_car-userdebug 27. aosp_oriole-userdebug 28. aosp_oriole_car-userdebug 29. aosp_panther-userdebug 30. aosp_raven-userdebug 31. aosp_raven_car-userdebug 32. aosp_ravenclaw-userdebug 33. aosp_redfin-userdebug 34. aosp_redfin_car-userdebug 35. aosp_redfin_vf-userdebug 36. aosp_rice14-userdebug 37. aosp_slider-userdebug 38. aosp_sunfish-userdebug 39. aosp_sunfish_car-userdebug 40. aosp_trout_arm64-userdebug 41. aosp_trout_x86-userdebug 42. aosp_whitefin-userdebug 43. aosp_x86-eng 44. aosp_x86_64-eng 45. arm_krait-eng 46. arm_v7_v8-eng 47. armv8-eng 48. armv8_cortex_a55-eng 49. armv8_kryo385-eng 50. beagle_x15-userdebug 51. beagle_x15_auto-userdebug 52. car_ui_portrait-userdebug 53. car_x86_64-userdebug 54. db845c-userdebug 55. gsi_car_arm64-userdebug 56. gsi_car_x86_64-userdebug 57. hikey-userdebug 58. hikey64_only-userdebug 59. hikey960-userdebug 60. hikey960_tv-userdebug 61. hikey_tv-userdebug 62. poplar-eng 63. poplar-user 64. poplar-userdebug 65. qemu_trusty_arm64-userdebug 66. rb5-userdebug 67. sdk_car_arm-userdebug 68. sdk_car_arm64-userdebug 69. sdk_car_portrait_x86_64-userdebug 70. sdk_car_x86-userdebug 71. sdk_car_x86_64-userdebug 72. sdk_pc_x86_64-userdebug 73. silvermont-eng 74. uml-userdebug 75. yukawa-userdebug 76. yukawa_sei510-userdebug Which would you like? [aosp_arm-eng] Pick from common choices above (e.g. 13) or specify your own (e.g. aosp_barbet-eng): 不同的product打包出来的镜像略有不同,最后在设备上展示的效果也不一样,比如,手机刷的镜像跟车载刷的镜像,Launcher,Settings等不尽相同。在上面lunch的product列表中,带有car标识的都是automotive product,其他的大部分是phone product。
-
automotive移植
就是将automotive的功能移植到非automotive的product上,即将非automotive product改造成automotive product,本篇我们将
72. sdk_pc_x86_64-userdebug
改造成71. sdk_car_x86_64-userdebug
,72. sdk_pc_x86_64-userdebug
长这样
现在我们要将它改造成上面automotive的样子
product编译配置
每个product的编译离不开四个文件:
AndroidProducts.mk
:定义product的名字,以及对应的Makefile文件product.mk
:对应product的Makefile文件,比如sdk_pc_x86_64.mk
,里面包含了product所有的软件配置BoardConfig.mk
:对应product的硬件配置信息device.mk
:芯片厂商自定义的硬件配置信息
看一下我们要改造的72. sdk_pc_x86_64-userdebug
这四个文件的内容
#AndroidProducts.mk PRODUCT_MAKEFILES := $(LOCAL_DIR)/sdk_pc_x86_64.mk COMMON_LUNCH_CHOICES := sdk_pc_x86_64-userdebug
COMMON_LUNCH_CHOICES
定义了lunch combo名称为sdk_pc_x86_64-userdebug
,该名称包括产品名+构建模式,userdebug表示调试模式,此外还有user模式和eng模式,关于这三个模式的区别,这里不做过多介绍,可自行查阅相关资料。PRODUCT_MAKEFILES
指定了product的makefile文件,$(LOCAL_DIR)
表示当前目录。
#sdk_pc_x86_64.mk PRODUCT_COPY_FILES += device/generic/goldfish/pc/config.ini.pc:config.ini $(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_x86_64.mk) PRODUCT_NAME := sdk_pc_x86_64 PRODUCT_DEVICE := emulator64_x86_64 PRODUCT_BRAND := Android PRODUCT_MODEL := PC on x86_64 emulator PRODUCT_PACKAGE_OVERLAYS := device/generic/goldfish/pc/overlay PRODUCT_COPY_FILES += device/generic/goldfish/pc/data/etc/pc.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/pc.xml PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP := device/generic/goldfish/pc/images_source.prop_template
sdk_pc_x86_64.mk
文件里定义了产品的name、device硬件配置、brand、model等信息,其中PRODUCT_DEVICE
指向BoardConfig.mk
和device.mk
的父目录
$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_x86_64.mk)
表示该文件继承自sdk_x86_64.mk
,$(SRC_TARGET_DIR)
指向build/target
目录。
PRODUCT_COPY_FILES
表示将源码中的文件复制到镜像分区中
#BoardConfig.mk # x86_64 emulator specific definitions TARGET_CPU_ABI := x86_64 TARGET_ARCH := x86_64 TARGET_ARCH_VARIANT := x86_64 TARGET_2ND_ARCH_VARIANT := x86_64 BOARD_DO_NOT_STRIP_VENDOR_MODULES := true TARGET_PRELINK_MODULE := false include build/make/target/board/BoardConfigGsiCommon.mk include build/make/target/board/BoardConfigEmuCommon.mk BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800 BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86 # Wifi. BOARD_WLAN_DEVICE := emulator BOARD_HOSTAPD_DRIVER := NL80211 BOARD_WPA_SUPPLICANT_DRIVER := NL80211 BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_simulated BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated WPA_SUPPLICANT_VERSION := VER_0_8_X WIFI_DRIVER_FW_PATH_PARAM := "/dev/null" WIFI_DRIVER_FW_PATH_STA := "/dev/null" WIFI_DRIVER_FW_PATH_AP := "/dev/null"
BoardConfig.mk
中定义了cpu的参数,wifi配置,并且通过include引用了BoardConfigGsiCommon.mk
和BoardConfigEmuCommon.mk
文件里的配置,前者用于通用系统映像的配置,后者用于模拟器的配置。
device.mk是芯片厂商自定义的配置,这里我们看的是原生代码,分析此文件没啥意义。
上述是72. sdk_pc_x86_64-userdebug
的编译配置文件,同理可析71. sdk_car_x86_64-userdebug
product编译流程
当我们执行source build/envsetup.sh
命令时,会将envsetup.sh
文件中定义的函数加载到当前shell环境,建立shell命令,比如lunch
、make
、croot
等。
当执行lunch
命令时,会执行envsetup.sh
中的lunch()
函数
function lunch() { # 定义局部变量`answer`来存放用户通过`lunch`命令传进来的参数(即`lunch-combo`,可以是字符串或数字) local answer if [[ $# -gt 1 ]]; then echo "usage: lunch [target]" >&2 return 1 fi local used_lunch_menu=0 if [ "$1" ]; then answer=$1 else # 如果用户没有没有传进来任何值,则打印出`lunch-combo`列表,提示用户输入 print_lunch_menu echo "Which would you like? [aosp_arm-eng]" echo -n "Pick from common choices above (e.g. 13) or specify your own (e.g. aosp_barbet-eng): " read answer used_lunch_menu=1 fi local selection= if [ -z "$answer" ] then #如果用户没有选择,则默认选择为aosp_arm-eng selection=aosp_arm-eng elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$") then local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES)) if [ $answer -le ${#choices[@]} ] then # array in zsh starts from 1 instead of 0. if [ -n "$ZSH_VERSION" ] then selection=${choices[$(($answer))]} else selection=${choices[$(($answer-1))]} fi fi else selection=$answer fi #省略部分代码 TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant TARGET_PLATFORM_VERSION=$version #build_build_var_cache()最终会执行到soong_ui.bash build_build_var_cache if [ $? -ne 0 ] then if [[ "$product" =~ .*_(eng|user|userdebug) ]] then echo "Did you mean -${product/*_/}? (dash instead of underscore)" fi return 1 fi #get_build_var()最终会执行到soong_ui.bash export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT) export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT) if [ -n "$version" ]; then export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION) else unset TARGET_PLATFORM_VERSION fi #省略部分代码 } # Get the exact value of a build variable. function get_build_var() { if [ "$BUILD_VAR_CACHE_READY" = "true" ] then eval "echo "${var_cache_$1}"" return 0 fi local T=$(gettop) if [ ! "$T" ]; then echo "Couldn't locate the top of the tree. Try setting TOP." >&2 return 1 fi #执行soong_ui.bash (cd $T; build/soong/soong_ui.bash --dumpvar-mode $1) }
lunch()函数最终会执行build/soong/soong_ui.bash
脚本
#记录了开始构建时的精确时间戳,以毫秒为单位 export TRACE_BEGIN_SOONG=$(date +%s%N) # Function to find top of the source tree (if $TOP isn't set) by walking up the # tree. #定义gettop函数 function gettop { #找到源代码树的根目录,如果环境变量 TOP 已经设置并且对应的路径存在 build/soong/root.bp 文件,那么函数将返回该路径;否则,它将从当前目录向上查找,直到找到名为 build/soong/root.bp 的文件。 local TOPFILE=build/soong/root.bp if [ -n "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then # The following circumlocution ensures we remove symlinks from TOP. (cd $TOP; PWD= /bin/pwd) else if [ -f $TOPFILE ] ; then # The following circumlocution (repeated below as well) ensures # that we record the true directory name and not one that is # faked up with symlink names. PWD= /bin/pwd else local HERE=$PWD T= while [ ( ! ( -f $TOPFILE ) ) -a ( $PWD != "/" ) ]; do cd .. T=`PWD= /bin/pwd -P` done cd $HERE if [ -f "$T/$TOPFILE" ]; then echo $T fi fi fi } #将当前工作目录(PWD)保存到环境变量 ORIGINAL_PWD 中 # Save the current PWD for use in soong_ui export ORIGINAL_PWD=${PWD} #调用 gettop 函数,并将返回的结果保存到环境变量 TOP 中 export TOP=$(gettop) #加载 microfactory.bash 脚本,该脚本应该位于 TOP 环境变量所指示的路径中 source ${TOP}/build/soong/scripts/microfactory.bash #调用 Android 构建系统的soong_ui,具体构建和运行某些组件 soong_build_go soong_ui android/soong/cmd/soong_ui soong_build_go mk2rbc android/soong/mk2rbc/cmd soong_build_go rbcrun rbcrun/cmd #将当前目录切换到 TOP 环境变量所指示的路径 cd ${TOP} #执行 soong_ui 程序,其路径为 getoutdir 环境变量的值,参数为 $@(表示所有传入的命令行参数) exec "$(getoutdir)/soong_ui" "$@"
上面在执行soong_build_go soong_ui android/soong/cmd/soong_ui
时,会执行/build/soong/cmd/soong_ui/main.go
中的main()
函数
func main() { //省略部分代码,省略的代码为构造build.Findsources()函数所需要的参数 // Create a source finder. f := build.NewSourceFinder(buildCtx, config) defer f.Shutdown() //寻找资源文件 build.FindSources(buildCtx, config, f) c.run(buildCtx, config, args, logsDir) }
在/build/soong/cmd/soong_ui/main.go
中的main()
函数中,核心主要是通过build.FindSources()
去寻找所有的makefile文件和bp文件
// FindSources searches for source files known to and writes them to the filesystem for // use later. func FindSources(ctx Context, config Config, f *finder.Finder) { // note that dumpDir in FindSources may be different than dumpDir in NewSourceFinder // if a caller such as multiproduct_kati wants to share one Finder among several builds dumpDir := config.FileListDir() os.MkdirAll(dumpDir, 0777) // Stop searching a subdirectory recursively after finding an Android.mk. //寻找所有的Android.mk文件,并添加到Android.mk.list中 androidMks := f.FindFirstNamedAt(".", "Android.mk") err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list")) if err != nil { ctx.Fatalf("Could not export module list: %v", err) } // Gate collecting/reporting mk metrics on builds that specifically request // it, as identifying the total number of mk files adds 4-5ms onto null // builds. if config.reportMkMetrics { androidMksTotal := f.FindNamedAt(".", "Android.mk") ctx.Metrics.SetToplevelMakefiles(len(androidMks)) ctx.Metrics.SetTotalMakefiles(len(androidMksTotal)) ctx.Metrics.DumpMkMetrics(config.MkMetrics()) } // Stop searching a subdirectory recursively after finding a CleanSpec.mk. //寻找所有的CleanSpec.mk文件,并添加到CleanSpec.mk.list中 cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk") err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list")) if err != nil { ctx.Fatalf("Could not export module list: %v", err) } // Only consider AndroidProducts.mk in device/, vendor/ and product/, recursively in these directories. //寻找所有的AndroidProducts.mk文件,并添加到AndroidProducts.mk.list中 androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk") androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...) androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...) err = dumpListToFile(ctx, config, androidProductsMks, filepath.Join(dumpDir, "AndroidProducts.mk.list")) if err != nil { ctx.Fatalf("Could not export product list: %v", err) } // Recursively look for all Bazel related files. bazelFiles := f.FindMatching(".", findBazelFiles) err = dumpListToFile(ctx, config, bazelFiles, filepath.Join(dumpDir, "bazel.list")) if err != nil { ctx.Fatalf("Could not export bazel BUILD list: %v", err) } // Recursively look for all OWNERS files. owners := f.FindNamedAt(".", "OWNERS") err = dumpListToFile(ctx, config, owners, filepath.Join(dumpDir, "OWNERS.list")) if err != nil { ctx.Fatalf("Could not find OWNERS: %v", err) } // Recursively look for all TEST_MAPPING files. testMappings := f.FindNamedAt(".", "TEST_MAPPING") err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list")) if err != nil { ctx.Fatalf("Could not find TEST_MAPPING: %v", err) } // Recursively look for all Android.bp files //寻找所有的Android.bp文件,并添加到Android.bp.list中 androidBps := f.FindNamedAt(".", "Android.bp") if len(androidBps) == 0 { ctx.Fatalf("No Android.bp found") } err = dumpListToFile(ctx, config, androidBps, filepath.Join(dumpDir, "Android.bp.list")) if err != nil { ctx.Fatalf("Could not find modules: %v", err) } // Recursively look for all product/board config files. configurationFiles := f.FindMatching(".", findProductAndBoardConfigFiles) err = dumpListToFile(ctx, config, configurationFiles, filepath.Join(dumpDir, "configuration.list")) if err != nil { ctx.Fatalf("Could not export product/board configuration list: %v", err) } if config.Dist() { f.WaitForDbDump() // Dist the files.db plain text database. distFile(ctx, config, f.DbPath, "module_paths") } }
执行完build.FindSources()
后,所有的AndroidProduct.mk
文件都将被发现,这时就能展示所有的lunch combo项,然后根据用户选择的combo,选择对应的AndroidProduct.mk
。
最后执行make -j16
命令后,调用build/envsetup.sh
里面的make()
函数,最终会根据对应AndroidProduct.mk
的规则,选择对应的product.mk
进行编译。
移植思路
了解了product编译流程后,自然而然我们能想到的移植思路是,在72. sdk_pc_x86_64-userdebug
的AndroidProduct.mk
文件里配置71. sdk_car_x86_64-userdebug
的product.mk,也就是将72. sdk_pc_x86_64-userdebug
的编译流程改向71. sdk_car_x86_64-userdebug
的编译流程,这样我们的目标product就会按照车载product的配置进行编译了。查看车载product的编译配置,即打开sdk_car_x86_64.mk
文件
PRODUCT_PACKAGE_OVERLAYS := device/generic/car/common/overlay $(call inherit-product, device/generic/car/emulator/aosp_car_emulator.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_x86_64.mk) EMULATOR_VENDOR_NO_SOUND := true PRODUCT_NAME := sdk_car_x86_64 PRODUCT_DEVICE := emulator_car_x86_64 PRODUCT_BRAND := Android PRODUCT_MODEL := Car on x86_64 emulator
可以看到,这里只是继承了device/generic/car/emulator/aosp_car_emulator.mk
和$(SRC_TARGET_DIR)/product/sdk_x86_64.mk
这两个makefile文件,以及添加了PRODUCT_PACKAGE_OVERLAYS
和EMULATOR_VENDOR_NO_SOUND
两项配置,我们将这些配置全部复制到sdk_pc_x86_64.mk
中
当然,PRODUCT_NAME
是不能变的,并且PRODUCT_DEVICE
选择车载的硬件配置。
最后执行编译
source build/envsetup.sh lunch sdk_pc_x86_64-userdebug make -j16
等待编译完成,启动模拟器
emulator
完美进入automotive,并且Model显示我们自定义的名称
参考资料
blog.csdn.net/daoshuti/ar…
写给应用开发的 Android Framework 教程——玩转 AOSP 篇之添加 Product